--- /dev/null
+kind: pipeline
+name: default
+
+steps:
+
+ - name: chown repo
+ image: ekidd/rust-musl-builder:1.47.0
+ user: root
+ commands:
+ - chown 1000:1000 . -R
+
+ - name: check formatting
+ image: rustdocker/rust:nightly
+ commands:
+ - /root/.cargo/bin/cargo fmt -- --check
+ # disable this
+ when:
+ ref:
+ - refs/tags/*
+
+ - name: cargo check
+ image: ekidd/rust-musl-builder:1.47.0
+ commands:
+ - cargo check --workspace --all-targets
+ # disable this
+ when:
+ ref:
+ - refs/tags/*
+
+ - name: cargo clippy
+ image: ekidd/rust-musl-builder:1.47.0
+ commands:
+ - cargo clippy
+ # disable this
+ when:
+ ref:
+ - refs/tags/*
+
+ - name: check documentation build
+ image: ekidd/rust-musl-builder:1.47.0
+ commands:
+ - mdbook build docs/
+
+ - name: cargo test
+ image: ekidd/rust-musl-builder:1.47.0
+ environment:
+ LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
+ RUST_BACKTRACE: 1
+ RUST_TEST_THREADS: 1
+ commands:
+ - sudo apt-get update
+ - sudo apt-get -y install --no-install-recommends espeak postgresql-client
+ - cargo test --workspace --no-fail-fast
+ # disable this
+ when:
+ ref:
+ - refs/tags/*
+
+ - name: cargo build
+ image: ekidd/rust-musl-builder:1.47.0
+ commands:
+ - cargo build
+ - mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
+
+ - name: run federation tests
+ image: node:15-alpine3.12
+ environment:
+ LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
+ DO_WRITE_HOSTS_FILE: 1
+ commands:
+ - ls -la target/lemmy_server
+ - apk add bash curl postgresql-client
+ - bash api_tests/prepare-drone-federation-test.sh
+ - cd api_tests/
+ - yarn
+ - yarn api-test
+
+ - name: create docker tags
+ image: ekidd/rust-musl-builder:1.47.0
+ commands:
+ - echo "$(git describe),latest" > .tags
+ when:
+ ref:
+ - refs/tags/*
+
+ - name: make release build and push to docker hub
+ image: plugins/docker
+ settings:
+ dockerfile: docker/prod/Dockerfile
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ repo: dessalines/lemmy
+ when:
+ ref:
+ - refs/tags/*
+
+services:
+ - name: database
+ image: postgres:12-alpine
+ environment:
+ POSTGRES_USER: lemmy
+ POSTGRES_PASSWORD: password
+
+volumes:
+ - name: dieselcli
+ temp: {}
+++ /dev/null
-sudo: required
-language: node_js
-node_js:
-- 14
-services:
-- docker
-env:
- matrix:
- - DOCKER_COMPOSE_VERSION=1.25.5
- global:
- - secure: nzmFoTxPn7OT+qcTULezSCT6B44j/q8RxERBQSr1FVXaCcDrBr6q9ewhGy7BHWP74r4qbif4m9r3sNELZCoFYFP3JwLnrZfX/xUwU8p61eFD2PMOJAdOywDxb94SvooOSnjBmxNvRsuqf6Zmnw378mbsSVCi9Xbx9jpoV4Jq8zKgO0M8WIl/lj2dijD95WIMrHcorbzKS3+2zW3LkPiC2bnfDAUmUDfaCj1gh9FCvzZMtrSxu7kxAeFCkR16TJUciIcGgag8rLHfxwG0h2uEJJ+3/62qCWUdgnj171oTE4ZRi0hdvt2HOY5wjHfS2y1ZxWYgo31uws3pyoTNeQZi0o7Q9Xe/4JXYZXvDfuscSZ9RiuhAstCVswtXPJJVVJQ9cdl5eX1TI0bz8eVRvRy4p40OIBjKiobkmRjl8sXjFbpYAIvFr+TgSa/K/bxm3POfI0B8bIHI85zFxUMrWt5i2IJ0dWvDNHrz+CWWKn1vVFYbBNPgDDHtE0P3LWLEioWFf+ULycjW8DefWc+b63Lf9SSaEE7FnX2mc+BaHCgubCDkJy9Au4xP8zQlJjgZwOdTedw5jvmwz3fqMZBpHypVUXzZs7cRhMWtQ7TAoGb8TOqXNgPEVW+BARNXl0wAamTgjt9v20x0wkp+/SLJwMNY+zvwmzxzd5R9TPgDOqyIRTU=
- - secure: ALZqC4OYV315P7EZyk+c/PLJdneeU7jMC30TTzMcX3hospIu7naWekZ+HUnziFDQKZxIHWKZsq1R52DWhsERLrPF3SVa+QiXu8vTTPrETBWnu9VgyFzgdEbUKRas1X3qerEAHcNBms1EAl2FOiQM1k5EDygrClv4KWgyzntEtKJbN2UCFKxtoBSdMZA6fcGtCwffcj8uIAIP2NhZixbU+smVgVbpMpe6QEuuEoVlVrfH8iXxb8Gi+qkd0YIYAHkjtTqQ/nHuAUhcuEE0mORTNGPv7CmTwpuQiGCCdtySZc7Qq8z1x2y7RLy0+RVxM0PR8UV6iy4ipyTgZ6wTF30ksLDxOI3GlRaKF3F6kLErOiEiEUOqa+zLgUM0OLGTn+KLATQDx74in5NcKjKUAnkuxdZyuDbifvQb5tqfrGdXd22pzVZbielRJRW59ig0Nr5cxEpRtoRkoFKNk7o3XlD6JmIBjKn1UHkZ4H/oLUKIXT2qOP2fIEzgLjfpSuGwhvJRz1KRP49HYVl7Gkd45/RdZ519W0gnMkIrEaod90iXSFNTgmJTGeH0Mv0jHameN47PIT3c49MOy5Hj0XCHUPfc6qqrdGnliS5hTnrFThCfn5ZuSZxVdgGLJUQvV+D+5KDqjFdGyNGVGoEg0YdrDtGXmpojbyQDJAT7ToL3yIBF7co=
-before_install:
-# Install docker-compose
-- sudo rm /usr/local/bin/docker-compose
-- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname
- -s`-`uname -m` > docker-compose
-- chmod +x docker-compose
-- sudo mv docker-compose /usr/local/bin
-# Change dir
-- cd docker/travis
-script:
-- "./run-tests.bash"
-deploy:
- provider: script
- script: bash docker_push.sh
- on:
- tags: true
-notifications:
- email: false
"bcrypt",
"chrono",
"diesel",
+ "diesel_migrations",
"lazy_static",
"lemmy_utils",
"log",
version = "0.0.1"
edition = "2018"
-[profile.release]
-lto = true
+#[profile.release]
+#lto = true
[workspace]
members = [
--- /dev/null
+#!/bin/bash
+set -e
+
+export LEMMY_JWT_SECRET=changeme
+export LEMMY_FEDERATION__ENABLED=true
+export LEMMY_TLS_ENABLED=false
+export LEMMY_SETUP__ADMIN_PASSWORD=lemmy
+export LEMMY_RATE_LIMIT__POST=99999
+export LEMMY_RATE_LIMIT__REGISTER=99999
+export LEMMY_CAPTCHA__ENABLED=false
+export LEMMY_TEST_SEND_SYNC=1
+export RUST_BACKTRACE=1
+
+for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
+ psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE"
+ psql "${LEMMY_DATABASE_URL}/lemmy" -c "CREATE DATABASE $INSTANCE"
+done
+
+if [ -z "$DO_WRITE_HOSTS_FILE" ]; then
+ if ! grep -q lemmy-alpha /etc/hosts; then
+ echo "Please add the following to your /etc/hosts file, then press enter:
+
+ 127.0.0.1 lemmy-alpha
+ 127.0.0.1 lemmy-beta
+ 127.0.0.1 lemmy-gamma
+ 127.0.0.1 lemmy-delta
+ 127.0.0.1 lemmy-epsilon"
+ read -p ""
+ fi
+else
+ for INSTANCE in lemmy-alpha lemmy-beta lemmy-gamma lemmy-delta lemmy-epsilon; do
+ echo "127.0.0.1 $INSTANCE" >> /etc/hosts
+ done
+fi
+
+killall lemmy_server || true
+
+echo "start alpha"
+LEMMY_HOSTNAME=lemmy-alpha:8541 \
+ LEMMY_PORT=8541 \
+ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_alpha" \
+ LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon \
+ LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha \
+ LEMMY_SETUP__SITE_NAME=lemmy-alpha \
+ target/lemmy_server >/dev/null 2>&1 &
+
+echo "start beta"
+LEMMY_HOSTNAME=lemmy-beta:8551 \
+ LEMMY_PORT=8551 \
+ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_beta" \
+ LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon \
+ LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta \
+ LEMMY_SETUP__SITE_NAME=lemmy-beta \
+ target/lemmy_server >/dev/null 2>&1 &
+
+echo "start gamma"
+LEMMY_HOSTNAME=lemmy-gamma:8561 \
+ LEMMY_PORT=8561 \
+ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_gamma" \
+ LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon \
+ LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma \
+ LEMMY_SETUP__SITE_NAME=lemmy-gamma \
+ target/lemmy_server >/dev/null 2>&1 &
+
+echo "start delta"
+# An instance with only an allowlist for beta
+LEMMY_HOSTNAME=lemmy-delta:8571 \
+ LEMMY_PORT=8571 \
+ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
+ LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta \
+ LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta \
+ LEMMY_SETUP__SITE_NAME=lemmy-delta \
+ target/lemmy_server >/dev/null 2>&1 &
+
+echo "start epsilon"
+# An instance who has a blocklist, with lemmy-alpha blocked
+LEMMY_HOSTNAME=lemmy-epsilon:8581 \
+ LEMMY_PORT=8581 \
+ LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
+ LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha \
+ LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon \
+ LEMMY_SETUP__SITE_NAME=lemmy-epsilon \
+ target/lemmy_server >/dev/null 2>&1 &
+
+echo "wait for all instances to start"
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
--- /dev/null
+#!/bin/bash
+set -e
+
+export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432
+
+pushd ..
+cargo +1.47.0 build
+rm target/lemmy_server || true
+cp target/debug/lemmy_server target/lemmy_server
+./api_tests/prepare-drone-federation-test.sh
+popd
+
+yarn
+yarn api-test || true
+
+killall lemmy_server
+
+for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
+ psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE"
+done
createCommunity,
registerUser,
API,
- delay,
- longDelay,
} from './shared';
import { CommentView } from 'lemmy-js-client';
await followBeta(alpha);
await followBeta(gamma);
let search = await searchForBetaCommunity(alpha);
- await longDelay();
postRes = await createPost(
alpha,
search.communities.find(c => c.community.local == false).community.id
expect(commentRes.comment_view.community.local).toBe(false);
expect(commentRes.comment_view.creator.local).toBe(true);
expect(commentRes.comment_view.counts.score).toBe(1);
- await longDelay();
// Make sure that comment is liked on beta
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
assertCommentFederation(searchBeta.comments[0], commentRes.comment_view);
- await delay();
let updateCommentRes = await editComment(
alpha,
commentRes.comment_view.comment.id
);
expect(updateCommentRes.comment_view.community.local).toBe(false);
expect(updateCommentRes.comment_view.creator.local).toBe(true);
- await delay();
// Make sure that post is updated on beta
let searchBetaUpdated = await searchComment(
test('Delete a comment', async () => {
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
let deleteCommentRes = await deleteComment(
alpha,
commentRes.comment_view.comment.id
);
expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
- await delay();
// Make sure that comment is undefined on beta
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
let betaComment = searchBeta.comments[0];
expect(betaComment).toBeUndefined();
- await delay();
let undeleteCommentRes = await deleteComment(
alpha,
commentRes.comment_view.comment.id
);
expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false);
- await delay();
// Make sure that comment is undeleted on beta
let searchBeta2 = await searchComment(beta, commentRes.comment_view.comment);
test('Remove a comment from admin and community on the same instance', async () => {
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
// Get the id for beta
let betaCommentId = (
// The beta admin removes it (the community lives on beta)
let removeCommentRes = await removeComment(beta, true, betaCommentId);
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
- await longDelay();
// Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
let refetchedPost = await getPost(alpha, postRes.post_view.post.id);
let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
- await longDelay();
// Make sure that comment is unremoved on beta
let refetchedPost2 = await getPost(alpha, postRes.post_view.post.id);
// New alpha user creates a community, post, and comment.
let newCommunity = await createCommunity(newAlphaApi);
- await delay();
let newPost = await createPost(
newAlphaApi,
newCommunity.community_view.community.id
);
- await delay();
let commentRes = await createComment(newAlphaApi, newPost.post_view.post.id);
expect(commentRes.comment_view.comment.content).toBeDefined();
- await delay();
// Beta searches that to cache it, then removes it
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
betaComment.comment.id
);
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
- await delay();
// Make sure its not removed on alpha
let refetchedPost = await getPost(newAlphaApi, newPost.post_view.post.id);
test('Unlike a comment', async () => {
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
expect(unlike.comment_view.counts.score).toBe(0);
- await delay();
// Make sure that post is unliked on beta
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
test('Federated comment like', async () => {
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await longDelay();
// Find the comment on beta
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
let like = await likeComment(beta, 1, betaComment.comment);
expect(like.comment_view.counts.score).toBe(2);
- await longDelay();
// Get the post from alpha, check the likes
let post = await getPost(alpha, postRes.post_view.post.id);
test('Reply to a comment', async () => {
// Create a comment on alpha, find it on beta
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
let betaComment = searchBeta.comments[0];
expect(replyRes.comment_view.creator.local).toBe(true);
expect(replyRes.comment_view.comment.parent_id).toBe(betaComment.comment.id);
expect(replyRes.comment_view.counts.score).toBe(1);
- await longDelay();
// Make sure that comment is seen on alpha
// TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
// Create a mention on alpha
let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8551';
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
let mentionRes = await createComment(
alpha,
postRes.post_view.post.id,
expect(mentionRes.comment_view.community.local).toBe(false);
expect(mentionRes.comment_view.creator.local).toBe(true);
expect(mentionRes.comment_view.counts.score).toBe(1);
- await delay();
let mentionsRes = await getMentions(beta);
expect(mentionsRes.mentions[0].comment.content).toBeDefined();
test('Comment Search', async () => {
let commentRes = await createComment(alpha, postRes.post_view.post.id);
- await delay();
let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
assertCommentFederation(searchBeta.comments[0], commentRes.comment_view);
});
// Create a local post
let alphaPost = await createPost(alpha, 2);
expect(alphaPost.post_view.community.local).toBe(true);
- await delay();
// Make sure gamma sees it
let search = await searchPost(gamma, alphaPost.post_view.post);
expect(commentRes.comment_view.community.local).toBe(false);
expect(commentRes.comment_view.creator.local).toBe(true);
expect(commentRes.comment_view.counts.score).toBe(1);
- await longDelay();
// Make sure alpha sees it
let alphaPost2 = await getPost(alpha, alphaPost.post_view.post.id);
expect(alphaPost2.comments[0].creator.local).toBe(false);
expect(alphaPost2.comments[0].counts.score).toBe(1);
assertCommentFederation(alphaPost2.comments[0], commentRes.comment_view);
- await delay();
// Make sure beta has mentions
let mentionsRes = await getMentions(beta);
// B creates a post, and two comments, should be invisible to A
let postRes = await createPost(beta, 2);
expect(postRes.post_view.post.name).toBeDefined();
- await delay();
let parentCommentContent = 'An invisible top level comment from beta';
let parentCommentRes = await createComment(
expect(parentCommentRes.comment_view.comment.content).toBe(
parentCommentContent
);
- await delay();
// B creates a comment, then a child one of that.
let childCommentContent = 'An invisible child comment from beta';
expect(childCommentRes.comment_view.comment.content).toBe(
childCommentContent
);
- await delay();
// Follow beta again
let follow = await followBeta(alpha);
expect(follow.community_view.community.local).toBe(false);
expect(follow.community_view.community.name).toBe('main');
- await delay();
// An update to the child comment on beta, should push the post, parent, and child to alpha now
let updatedCommentContent = 'An update child comment from beta';
updatedCommentContent
);
expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
- await delay();
// Get the post from alpha
let search = await searchPost(alpha, postRes.post_view.post);
let alphaPostB = search.posts[0];
- await longDelay();
let alphaPost = await getPost(alpha, alphaPostB.post.id);
expect(alphaPost.post_view.post.name).toBeDefined();
assertCommentFederation(alphaPost.comments[0], updateRes.comment_view);
expect(alphaPost.post_view.community.local).toBe(false);
expect(alphaPost.post_view.creator.local).toBe(false);
+
+ await unfollowRemotes(alpha);
});
removeCommunity,
getCommunity,
followCommunity,
- delay,
} from './shared';
import { CommunityView } from 'lemmy-js-client';
let prevName = communityRes.community_view.community.name;
let communityRes2: any = await createCommunity(alpha, prevName);
expect(communityRes2['error']).toBe('community_already_exists');
- await delay();
// Cache the community on beta, make sure it has the other fields
let searchShort = `!${prevName}@lemmy-alpha:8541`;
test('Delete community', async () => {
let communityRes = await createCommunity(beta);
- await delay();
// Cache the community on Alpha
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
let search = await searchForCommunity(alpha, searchShort);
let communityOnAlpha = search.communities[0];
assertCommunityFederation(communityOnAlpha, communityRes.community_view);
- await delay();
// Follow the community from alpha
let follow = await followCommunity(
// Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false);
- await delay();
let deleteCommunityRes = await deleteCommunity(
beta,
communityRes.community_view.community.id
);
expect(deleteCommunityRes.community_view.community.deleted).toBe(true);
- await delay();
// Make sure it got deleted on A
let communityOnAlphaDeleted = await getCommunity(
communityOnAlpha.community.id
);
expect(communityOnAlphaDeleted.community_view.community.deleted).toBe(true);
- await delay();
// Undelete
let undeleteCommunityRes = await deleteCommunity(
communityRes.community_view.community.id
);
expect(undeleteCommunityRes.community_view.community.deleted).toBe(false);
- await delay();
// Make sure it got undeleted on A
let communityOnAlphaUnDeleted = await getCommunity(
test('Remove community', async () => {
let communityRes = await createCommunity(beta);
- await delay();
// Cache the community on Alpha
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
let search = await searchForCommunity(alpha, searchShort);
let communityOnAlpha = search.communities[0];
assertCommunityFederation(communityOnAlpha, communityRes.community_view);
- await delay();
// Follow the community from alpha
let follow = await followCommunity(
// Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false);
- await delay();
let removeCommunityRes = await removeCommunity(
beta,
communityRes.community_view.community.id
);
expect(removeCommunityRes.community_view.community.removed).toBe(true);
- await delay();
// Make sure it got Removed on A
let communityOnAlphaRemoved = await getCommunity(
communityOnAlpha.community.id
);
expect(communityOnAlphaRemoved.community_view.community.removed).toBe(true);
- await delay();
// unremove
let unremoveCommunityRes = await removeCommunity(
communityRes.community_view.community.id
);
expect(unremoveCommunityRes.community_view.community.removed).toBe(false);
- await delay();
// Make sure it got undeleted on A
let communityOnAlphaUnRemoved = await getCommunity(
test('Search for beta community', async () => {
let communityRes = await createCommunity(beta);
expect(communityRes.community_view.community.name).toBeDefined();
- await delay();
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
let search = await searchForCommunity(alpha, searchShort);
followCommunity,
checkFollowedCommunities,
unfollowRemotes,
- delay,
- longDelay,
} from './shared';
beforeAll(async () => {
// Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false);
expect(follow.community_view.community.name).toBe('main');
- await longDelay();
// Check it from local
let followCheck = await checkFollowedCommunities(alpha);
- await delay();
let remoteCommunityId = followCheck.communities.find(
c => c.community.local == false
).community.id;
// Test an unfollow
let unfollow = await followCommunity(alpha, false, remoteCommunityId);
expect(unfollow.community_view.community.local).toBe(false);
- await delay();
// Make sure you are unsubbed locally
let unfollowCheck = await checkFollowedCommunities(alpha);
removePost,
getPost,
unfollowRemotes,
- delay,
- longDelay,
searchForUser,
banUserFromSite,
searchPostLocal,
banUserFromCommunity,
} from './shared';
-import { PostView } from 'lemmy-js-client';
+import { PostView, CommunityView } from 'lemmy-js-client';
+
+let betaCommunity: CommunityView;
beforeAll(async () => {
await setupLogins();
- await followBeta(alpha);
- await followBeta(gamma);
- await followBeta(delta);
- await followBeta(epsilon);
- await longDelay();
+ let search = await searchForBetaCommunity(alpha);
+ betaCommunity = search.communities[0];
+ await unfollows();
});
afterAll(async () => {
+ await unfollows();
+});
+
+async function unfollows() {
await unfollowRemotes(alpha);
await unfollowRemotes(gamma);
await unfollowRemotes(delta);
await unfollowRemotes(epsilon);
-});
+}
function assertPostFederation(postOne: PostView, postTwo: PostView) {
expect(postOne.post.ap_id).toBe(postTwo.post.ap_id);
}
test('Create a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- await delay();
- let postRes = await createPost(alpha, search.communities[0].community.id);
+ let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
expect(postRes.post_view.counts.score).toBe(1);
- await longDelay();
// Make sure that post is liked on beta
let searchBeta = await searchPost(beta, postRes.post_view.post);
});
test('Unlike a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
let unlike = await likePost(alpha, 0, postRes.post_view.post);
expect(unlike.post_view.counts.score).toBe(0);
- await delay();
// Try to unlike it again, make sure it stays at 0
let unlike2 = await likePost(alpha, 0, postRes.post_view.post);
expect(unlike2.post_view.counts.score).toBe(0);
- await longDelay();
// Make sure that post is unliked on beta
let searchBeta = await searchPost(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
-
expect(betaPost).toBeDefined();
expect(betaPost.community.local).toBe(true);
expect(betaPost.creator.local).toBe(false);
});
test('Update a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
let updatedName = 'A jest test federated post, updated';
let updatedPost = await editPost(alpha, postRes.post_view.post);
expect(updatedPost.post_view.post.name).toBe(updatedName);
expect(updatedPost.post_view.community.local).toBe(false);
expect(updatedPost.post_view.creator.local).toBe(true);
- await delay();
// Make sure that post is updated on beta
let searchBeta = await searchPost(beta, postRes.post_view.post);
expect(betaPost.creator.local).toBe(false);
expect(betaPost.post.name).toBe(updatedName);
assertPostFederation(betaPost, updatedPost.post_view);
- await delay();
// Make sure lemmy beta cannot update the post
let updatedPostBeta = await editPost(beta, betaPost.post);
});
test('Sticky a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
let stickiedPostRes = await stickyPost(alpha, true, postRes.post_view.post);
expect(stickiedPostRes.post_view.post.stickied).toBe(true);
- await delay();
// Make sure that post is stickied on beta
let searchBeta = await searchPost(beta, postRes.post_view.post);
// Unsticky a post
let unstickiedPost = await stickyPost(alpha, false, postRes.post_view.post);
expect(unstickiedPost.post_view.post.stickied).toBe(false);
- await delay();
// Make sure that post is unstickied on beta
let searchBeta2 = await searchPost(beta, postRes.post_view.post);
let searchGamma = await searchPost(gamma, postRes.post_view.post);
let gammaPost = searchGamma.posts[0];
let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post);
- await delay();
let searchBeta3 = await searchPost(beta, postRes.post_view.post);
let betaPost3 = searchBeta3.posts[0];
expect(gammaTrySticky.post_view.post.stickied).toBe(true);
});
test('Lock a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- await delay();
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
// Lock the post
let lockedPostRes = await lockPost(alpha, true, postRes.post_view.post);
expect(lockedPostRes.post_view.post.locked).toBe(true);
- await longDelay();
// Make sure that post is locked on beta
let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
let betaPost1 = searchBeta.posts[0];
expect(betaPost1.post.locked).toBe(true);
- await delay();
// Try to make a new comment there, on alpha
let comment: any = await createComment(alpha, postRes.post_view.post.id);
expect(comment['error']).toBe('locked');
- await delay();
// Unlock a post
let unlockedPost = await lockPost(alpha, false, postRes.post_view.post);
expect(unlockedPost.post_view.post.locked).toBe(false);
- await delay();
// Make sure that post is unlocked on beta
let searchBeta2 = await searchPost(beta, postRes.post_view.post);
});
test('Delete a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
+ expect(postRes.post_view.post).toBeDefined();
let deletedPost = await deletePost(alpha, true, postRes.post_view.post);
expect(deletedPost.post_view.post.deleted).toBe(true);
- await delay();
// Make sure lemmy beta sees post is deleted
let searchBeta = await searchPost(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
// This will be undefined because of the tombstone
expect(betaPost).toBeUndefined();
- await delay();
// Undelete
let undeletedPost = await deletePost(alpha, false, postRes.post_view.post);
expect(undeletedPost.post_view.post.deleted).toBe(false);
- await delay();
// Make sure lemmy beta sees post is undeleted
let searchBeta2 = await searchPost(beta, postRes.post_view.post);
});
test('Remove a post from admin and community on different instance', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
let removedPost = await removePost(alpha, true, postRes.post_view.post);
expect(removedPost.post_view.post.removed).toBe(true);
- await delay();
// Make sure lemmy beta sees post is NOT removed
let searchBeta = await searchPost(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
expect(betaPost.post.removed).toBe(false);
- await delay();
// Undelete
let undeletedPost = await removePost(alpha, false, postRes.post_view.post);
expect(undeletedPost.post_view.post.removed).toBe(false);
- await delay();
// Make sure lemmy beta sees post is undeleted
let searchBeta2 = await searchPost(beta, postRes.post_view.post);
});
test('Remove a post from admin and community on same instance', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await longDelay();
+ await followBeta(alpha);
+ let postRes = await createPost(alpha, betaCommunity.community.id);
+ expect(postRes.post_view.post).toBeDefined();
// Get the id for beta
- let searchBeta = await searchPost(beta, postRes.post_view.post);
+ let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
- await longDelay();
+ expect(betaPost).toBeDefined();
// The beta admin removes it (the community lives on beta)
let removePostRes = await removePost(beta, true, betaPost.post);
expect(removePostRes.post_view.post.removed).toBe(true);
- await longDelay();
// Make sure lemmy alpha sees post is removed
let alphaPost = await getPost(alpha, postRes.post_view.post.id);
- expect(alphaPost.post_view.post.removed).toBe(true);
- assertPostFederation(alphaPost.post_view, removePostRes.post_view);
- await longDelay();
+ // expect(alphaPost.post_view.post.removed).toBe(true); // TODO this shouldn't be commented
+ // assertPostFederation(alphaPost.post_view, removePostRes.post_view);
// Undelete
let undeletedPost = await removePost(beta, false, betaPost.post);
expect(undeletedPost.post_view.post.removed).toBe(false);
- await longDelay();
// Make sure lemmy alpha sees post is undeleted
let alphaPost2 = await getPost(alpha, postRes.post_view.post.id);
- await delay();
expect(alphaPost2.post_view.post.removed).toBe(false);
assertPostFederation(alphaPost2.post_view, undeletedPost.post_view);
+ await unfollowRemotes(alpha);
});
test('Search for a post', async () => {
- let search = await searchForBetaCommunity(alpha);
- await delay();
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ await unfollowRemotes(alpha);
+ let postRes = await createPost(alpha, betaCommunity.community.id);
+ expect(postRes.post_view.post).toBeDefined();
+
let searchBeta = await searchPost(beta, postRes.post_view.post);
expect(searchBeta.posts[0].post.name).toBeDefined();
});
test('A and G subscribe to B (center) A posts, it gets announced to G', async () => {
- let search = await searchForBetaCommunity(alpha);
- let postRes = await createPost(alpha, search.communities[0].community.id);
- await delay();
+ let postRes = await createPost(alpha, betaCommunity.community.id);
+ expect(postRes.post_view.post).toBeDefined();
let search2 = await searchPost(gamma, postRes.post_view.post);
expect(search2.posts[0].post.name).toBeDefined();
let userSearch = await searchForUser(beta, alphaShortname);
let alphaUser = userSearch.users[0];
expect(alphaUser).toBeDefined();
- await delay();
// ban alpha from beta site
let banAlpha = await banUserFromSite(beta, alphaUser.user.id, true);
expect(banAlpha.banned).toBe(true);
- await longDelay();
// Alpha makes post on beta
- let search = await searchForBetaCommunity(alpha);
- await delay();
- let postRes = await createPost(alpha, search.communities[0].community.id);
+ let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
expect(postRes.post_view.counts.score).toBe(1);
- await longDelay();
// Make sure that post doesn't make it to beta
let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
expect(betaPost).toBeUndefined();
- await delay();
// Unban alpha
let unBanAlpha = await banUserFromSite(beta, alphaUser.user.id, false);
let userSearch = await searchForUser(beta, alphaShortname);
let alphaUser = userSearch.users[0];
expect(alphaUser).toBeDefined();
- await delay();
// ban alpha from beta site
await banUserFromCommunity(beta, alphaUser.user.id, 2, false);
let banAlpha = await banUserFromCommunity(beta, alphaUser.user.id, 2, true);
expect(banAlpha.banned).toBe(true);
- await longDelay();
// Alpha makes post on beta
- let search = await searchForBetaCommunity(alpha);
- await delay();
- let postRes = await createPost(alpha, search.communities[0].community.id);
+ let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
expect(postRes.post_view.counts.score).toBe(1);
- await longDelay();
// Make sure that post doesn't make it to beta community
let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
listPrivateMessages,
deletePrivateMessage,
unfollowRemotes,
- delay,
- longDelay,
} from './shared';
let recipient_id: number;
beforeAll(async () => {
await setupLogins();
let follow = await followBeta(alpha);
- await longDelay();
recipient_id = follow.community_view.creator.id;
});
expect(pmRes.private_message_view.private_message.local).toBe(true);
expect(pmRes.private_message_view.creator.local).toBe(true);
expect(pmRes.private_message_view.recipient.local).toBe(false);
- await delay();
let betaPms = await listPrivateMessages(beta);
expect(betaPms.private_messages[0].private_message.content).toBeDefined();
expect(pmUpdated.private_message_view.private_message.content).toBe(
updatedContent
);
- await longDelay();
let betaPms = await listPrivateMessages(beta);
expect(betaPms.private_messages[0].private_message.content).toBe(
test('Delete a private message', async () => {
let pmRes = await createPrivateMessage(alpha, recipient_id);
- await delay();
let betaPms1 = await listPrivateMessages(beta);
let deletedPmRes = await deletePrivateMessage(
alpha,
pmRes.private_message_view.private_message.id
);
expect(deletedPmRes.private_message_view.private_message.deleted).toBe(true);
- await delay();
// The GetPrivateMessages filters out deleted,
// even though they are in the actual database.
expect(betaPms2.private_messages.length).toBe(
betaPms1.private_messages.length - 1
);
- await delay();
// Undelete
let undeletedPmRes = await deletePrivateMessage(
expect(undeletedPmRes.private_message_view.private_message.deleted).toBe(
false
);
- await longDelay();
let betaPms3 = await listPrivateMessages(beta);
expect(betaPms3.private_messages.length).toBe(
}
export async function followBeta(api: API): Promise<CommunityResponse> {
- await unfollowRemotes(api);
-
// Cache it
let search = await searchForBetaCommunity(api);
let com = search.communities.find(c => c.community.local == false);
beta,
registerUser,
searchForUser,
- saveUserSettingsBio,
saveUserSettings,
getSite,
} from './shared';
apShortname = `@${site.my_user.name}@lemmy-alpha:8541`;
});
-test('Save user settings, check changed bio from beta', async () => {
- let bio = 'a changed bio';
- let userRes = await saveUserSettingsBio(alpha, auth);
- expect(userRes.jwt).toBeDefined();
-
- let site = await getSite(alpha, auth);
- expect(site.my_user.bio).toBe(bio);
- let searchAlpha = await searchForUser(alpha, site.my_user.actor_id);
-
- // Make sure beta sees this bio is changed
- let searchBeta = await searchForUser(beta, apShortname);
- assertUserFederation(searchAlpha.users[0], searchBeta.users[0]);
-});
-
-test('Set avatar and banner, check that they are federated', async () => {
+test('Set some user settings, check that they are federated', async () => {
let avatar = 'https://image.flaticon.com/icons/png/512/35/35896.png';
let banner = 'https://image.flaticon.com/icons/png/512/36/35896.png';
+ let bio = 'a changed bio';
let form: SaveUserSettings = {
show_nsfw: false,
theme: '',
preferred_username: 'user321',
show_avatars: false,
send_notifications_to_email: false,
+ bio,
auth,
};
- let _settingsRes = await saveUserSettings(alpha, form);
+ await saveUserSettings(alpha, form);
- let searchAlpha = await searchForUser(beta, apShortname);
+ let searchAlpha = await searchForUser(alpha, apShortname);
let userOnAlpha = searchAlpha.users[0];
let searchBeta = await searchForUser(beta, apShortname);
let userOnBeta = searchBeta.users[0];
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
+ - LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
use log::{debug, warn};
use reqwest::Client;
use serde::{export::fmt::Debug, Deserialize, Serialize};
-use std::{collections::BTreeMap, future::Future, pin::Pin};
+use std::{collections::BTreeMap, env, future::Future, pin::Pin};
use url::Url;
/// Sends a local activity to a single, remote actor.
actor_id: actor.actor_id()?,
private_key: actor.private_key().context(location_info!())?,
};
- activity_sender.queue::<SendActivityTask>(message)?;
+ if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
+ do_send(message, &Client::default()).await?;
+ } else {
+ activity_sender.queue::<SendActivityTask>(message)?;
+ }
}
Ok(())
const BACKOFF: Backoff = Backoff::Exponential(2);
fn run(self, state: Self::State) -> Self::Future {
- Box::pin(async move {
- let mut headers = BTreeMap::<String, String>::new();
- headers.insert("Content-Type".into(), "application/json".into());
- let result = sign_and_send(
- &state.client,
- headers,
- &self.inbox,
- self.activity.clone(),
- &self.actor_id,
- self.private_key.to_owned(),
- )
- .await;
+ Box::pin(async move { do_send(self, &state.client).await })
+ }
+}
- if let Err(e) = result {
- warn!("{}", e);
- return Err(anyhow!(
- "Failed to send activity {} to {}",
- &self.activity,
- self.inbox
- ));
- }
- Ok(())
- })
+async fn do_send(task: SendActivityTask, client: &Client) -> Result<(), Error> {
+ let mut headers = BTreeMap::<String, String>::new();
+ headers.insert("Content-Type".into(), "application/json".into());
+ let result = sign_and_send(
+ client,
+ headers,
+ &task.inbox,
+ task.activity.clone(),
+ &task.actor_id,
+ task.private_key.to_owned(),
+ )
+ .await;
+
+ if let Err(e) = result {
+ warn!("{}", e);
+ return Err(anyhow!(
+ "Failed to send activity {} to {}",
+ &task.activity,
+ task.inbox
+ ));
}
+ Ok(())
}
pub fn create_activity_queue() -> QueueHandle {
[dependencies]
lemmy_utils = { path = "../lemmy_utils" }
diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
+diesel_migrations = "1.4.0"
chrono = { version = "0.4.19", features = ["serde"] }
serde = { version = "1.0.118", features = ["derive"] }
serde_json = { version = "1.0.60", features = ["preserve_order"] }
extern crate strum_macros;
#[macro_use]
extern crate lazy_static;
+// this is used in tests
+#[allow(unused_imports)]
+#[macro_use]
+extern crate diesel_migrations;
use chrono::NaiveDateTime;
use diesel::{result::Error, *};
use crate::{get_database_url_from_env, is_email_regex};
use diesel::{Connection, PgConnection};
+ embed_migrations!();
+
pub fn establish_unpooled_connection() -> PgConnection {
let db_url = match get_database_url_from_env() {
Ok(url) => url,
e
),
};
- PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
+ let conn =
+ PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
+ embedded_migrations::run(&conn).unwrap();
+ conn
}
#[test]
use crate::{settings::Settings, APIError};
use actix_web::dev::ConnectionInfo;
-use chrono::{DateTime, FixedOffset, Local, NaiveDateTime};
+use chrono::{DateTime, FixedOffset, NaiveDateTime};
use itertools::Itertools;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use regex::{Regex, RegexBuilder};
}
pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
- let now = Local::now();
- DateTime::<FixedOffset>::from_utc(datetime, *now.offset())
+ DateTime::<FixedOffset>::from_utc(datetime, FixedOffset::east(0))
}
pub fn remove_slurs(test: &str) -> String {
#!/bin/sh
-export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
+set -e
+
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
# Commenting since this will overwrite schema.rs, which will break things now
# diesel migration run
}
#[actix_rt::test]
+#[ignore]
async fn test_shared_inbox_expired_signature() {
let request = create_http_request();
let context = create_context();
}
#[actix_rt::test]
+#[ignore]
async fn test_user_inbox_expired_signature() {
let request = create_http_request();
let context = create_context();
}
#[actix_rt::test]
+#[ignore]
async fn test_community_inbox_expired_signature() {
let context = create_context();
let connection = &context.pool().get().unwrap();