Merge branch 'main' into fix-local-follow

This commit is contained in:
Felix Ableitner 2023-11-17 10:30:07 +01:00
commit 920dbabe84
61 changed files with 3877 additions and 774 deletions

2
.gitignore vendored
View file

@ -20,6 +20,8 @@ query_testing/**/reports/*.json
api_tests/node_modules api_tests/node_modules
api_tests/.yalc api_tests/.yalc
api_tests/yalc.lock api_tests/yalc.lock
api_tests/test.png
api_tests/pict-rs
# pictrs data # pictrs data
pictrs/ pictrs/

41
Cargo.lock generated
View file

@ -1997,9 +1997,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -2531,7 +2531,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "lemmy_api" name = "lemmy_api"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-web", "actix-web",
@ -2559,11 +2559,10 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_api_common" name = "lemmy_api_common"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-web", "actix-web",
"anyhow",
"chrono", "chrono",
"encoding", "encoding",
"enum-map", "enum-map",
@ -2594,7 +2593,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_api_crud" name = "lemmy_api_crud"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-web", "actix-web",
@ -2612,7 +2611,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_apub" name = "lemmy_apub"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-web", "actix-web",
@ -2651,7 +2650,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_db_schema" name = "lemmy_db_schema"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"async-trait", "async-trait",
@ -2687,7 +2686,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_db_views" name = "lemmy_db_views"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"diesel", "diesel",
@ -2705,7 +2704,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_db_views_actor" name = "lemmy_db_views_actor"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"chrono", "chrono",
"diesel", "diesel",
@ -2722,7 +2721,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_db_views_moderator" name = "lemmy_db_views_moderator"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"diesel", "diesel",
"diesel-async", "diesel-async",
@ -2734,7 +2733,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_federate" name = "lemmy_federate"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"anyhow", "anyhow",
@ -2758,7 +2757,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_routes" name = "lemmy_routes"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-web", "actix-web",
@ -2782,7 +2781,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_server" name = "lemmy_server"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"activitypub_federation", "activitypub_federation",
"actix-cors", "actix-cors",
@ -2823,7 +2822,7 @@ dependencies = [
[[package]] [[package]]
name = "lemmy_utils" name = "lemmy_utils"
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"anyhow", "anyhow",
@ -3307,9 +3306,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.57" version = "0.10.59"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
"cfg-if", "cfg-if",
@ -3339,9 +3338,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.93" version = "0.9.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -5357,9 +5356,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.9" version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",

View file

@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "0.19.0-rc.4" version = "0.19.0-rc.5"
edition = "2021" edition = "2021"
description = "A link aggregator for the fediverse" description = "A link aggregator for the fediverse"
license = "AGPL-3.0" license = "AGPL-3.0"
@ -59,16 +59,16 @@ members = [
] ]
[workspace.dependencies] [workspace.dependencies]
lemmy_api = { version = "=0.19.0-rc.4", path = "./crates/api" } lemmy_api = { version = "=0.19.0-rc.5", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.0-rc.4", path = "./crates/api_crud" } lemmy_api_crud = { version = "=0.19.0-rc.5", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.0-rc.4", path = "./crates/apub" } lemmy_apub = { version = "=0.19.0-rc.5", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.0-rc.4", path = "./crates/utils" } lemmy_utils = { version = "=0.19.0-rc.5", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.0-rc.4", path = "./crates/db_schema" } lemmy_db_schema = { version = "=0.19.0-rc.5", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.0-rc.4", path = "./crates/api_common" } lemmy_api_common = { version = "=0.19.0-rc.5", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.0-rc.4", path = "./crates/routes" } lemmy_routes = { version = "=0.19.0-rc.5", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.0-rc.4", path = "./crates/db_views" } lemmy_db_views = { version = "=0.19.0-rc.5", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.0-rc.4", path = "./crates/db_views_actor" } lemmy_db_views_actor = { version = "=0.19.0-rc.5", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.0-rc.4", path = "./crates/db_views_moderator" } lemmy_db_views_moderator = { version = "=0.19.0-rc.5", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.0-beta.4", default-features = false, features = [ activitypub_federation = { version = "0.5.0-beta.4", default-features = false, features = [
"actix-web", "actix-web",
] } ] }
@ -138,7 +138,7 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true } lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true } lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true } lemmy_routes = { workspace = true }
lemmy_federate = { version = "0.19.0-rc.4", path = "crates/federate" } lemmy_federate = { version = "0.19.0-rc.5", path = "crates/federate" }
activitypub_federation = { workspace = true } activitypub_federation = { workspace = true }
diesel = { workspace = true } diesel = { workspace = true }
diesel-async = { workspace = true } diesel-async = { workspace = true }

View file

@ -9,23 +9,25 @@
"scripts": { "scripts": {
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'", "lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
"fix": "prettier --write src && eslint --fix src", "fix": "prettier --write src && eslint --fix src",
"api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts", "api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts && jest -i image.spec.ts",
"api-test-follow": "jest -i follow.spec.ts", "api-test-follow": "jest -i follow.spec.ts",
"api-test-comment": "jest -i comment.spec.ts", "api-test-comment": "jest -i comment.spec.ts",
"api-test-post": "jest -i post.spec.ts", "api-test-post": "jest -i post.spec.ts",
"api-test-user": "jest -i user.spec.ts", "api-test-user": "jest -i user.spec.ts",
"api-test-community": "jest -i community.spec.ts", "api-test-community": "jest -i community.spec.ts",
"api-test-private-message": "jest -i private_message.spec.ts" "api-test-private-message": "jest -i private_message.spec.ts",
"api-test-image": "jest -i image.spec.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.8", "@types/jest": "^29.5.8",
"@types/node": "^20.9.0", "@types/node": "^20.9.0",
"@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0", "@typescript-eslint/parser": "^6.10.0",
"download-file-sync": "^1.0.4",
"eslint": "^8.53.0", "eslint": "^8.53.0",
"eslint-plugin-prettier": "^5.0.1", "eslint-plugin-prettier": "^5.0.1",
"jest": "^29.5.0", "jest": "^29.5.0",
"lemmy-js-client": "0.19.0-rc.12", "lemmy-js-client": "0.19.0-alpha.18",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"ts-jest": "^29.1.0", "ts-jest": "^29.1.0",
"typescript": "^5.0.4" "typescript": "^5.0.4"

BIN
api_tests/pict-rs Executable file

Binary file not shown.

View file

@ -8,6 +8,17 @@ export RUST_LOG="warn,lemmy_server=debug,lemmy_federate=debug,lemmy_api=debug,le
export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min
# pictrs setup
if ! [ -f "pict-rs" ]; then
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.0-beta.2/pict-rs-linux-amd64" -o api_tests/pict-rs
chmod +x api_tests/pict-rs
fi
./api_tests/pict-rs \
run -a 0.0.0.0:8080 \
--danger-dummy-mode \
filesystem -p /tmp/pictrs/files \
sled -p /tmp/pictrs/sled-repo 2>&1 &
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
echo "DB URL: ${LEMMY_DATABASE_URL} INSTANCE: $INSTANCE" echo "DB URL: ${LEMMY_DATABASE_URL} INSTANCE: $INSTANCE"
psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE" psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE"

View file

@ -14,6 +14,8 @@ yarn
yarn api-test || true yarn api-test || true
killall -s1 lemmy_server || true killall -s1 lemmy_server || true
killall -s1 pict-rs || true
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE" psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE"
done done
rm -r /tmp/pictrs

View file

@ -54,8 +54,8 @@ beforeAll(async () => {
} }
}); });
afterAll(async () => { afterAll(() => {
await unfollows(); unfollows();
}); });
function assertCommentFederation( function assertCommentFederation(
@ -94,7 +94,9 @@ test("Create a comment", async () => {
}); });
test("Create a comment in a non-existent post", async () => { test("Create a comment in a non-existent post", async () => {
await expect(createComment(alpha, -1)).rejects.toBe("couldnt_find_post"); await expect(createComment(alpha, -1)).rejects.toStrictEqual(
Error("couldnt_find_post"),
);
}); });
test("Update a comment", async () => { test("Update a comment", async () => {
@ -143,7 +145,7 @@ test("Delete a comment", async () => {
await waitUntil( await waitUntil(
() => () =>
resolveComment(gamma, commentRes.comment_view.comment).catch(e => e), resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
r => r !== "couldnt_find_object", r => r.message !== "couldnt_find_object",
) )
).comment; ).comment;
if (!gammaComment) { if (!gammaComment) {
@ -160,13 +162,13 @@ test("Delete a comment", async () => {
// Make sure that comment is undefined on beta // Make sure that comment is undefined on beta
await waitUntil( await waitUntil(
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e), () => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
e => e === "couldnt_find_object", e => e.message == "couldnt_find_object",
); );
// Make sure that comment is undefined on gamma after delete // Make sure that comment is undefined on gamma after delete
await waitUntil( await waitUntil(
() => resolveComment(gamma, commentRes.comment_view.comment).catch(e => e), () => resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
e => e === "couldnt_find_object", e => e.message === "couldnt_find_object",
); );
// Test undeleting the comment // Test undeleting the comment
@ -181,7 +183,7 @@ test("Delete a comment", async () => {
let betaComment2 = ( let betaComment2 = (
await waitUntil( await waitUntil(
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e), () => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
e => e !== "couldnt_find_object", e => e.message !== "couldnt_find_object",
) )
).comment; ).comment;
expect(betaComment2?.comment.deleted).toBe(false); expect(betaComment2?.comment.deleted).toBe(false);

View file

@ -34,9 +34,7 @@ import {
} from "./shared"; } from "./shared";
import { EditSite, LemmyHttp } from "lemmy-js-client"; import { EditSite, LemmyHttp } from "lemmy-js-client";
beforeAll(async () => { beforeAll(setupLogins);
await setupLogins();
});
function assertCommunityFederation( function assertCommunityFederation(
communityOne?: CommunityView, communityOne?: CommunityView,
@ -66,8 +64,8 @@ test("Create community", async () => {
// A dupe check // A dupe check
let prevName = communityRes.community_view.community.name; let prevName = communityRes.community_view.community.name;
await expect(createCommunity(alpha, prevName)).rejects.toBe( await expect(createCommunity(alpha, prevName)).rejects.toStrictEqual(
"community_already_exists", Error("community_already_exists"),
); );
// Cache the community on beta, make sure it has the other fields // Cache the community on beta, make sure it has the other fields
@ -333,8 +331,8 @@ test("Get community for different casing on domain", async () => {
// A dupe check // A dupe check
let prevName = communityRes.community_view.community.name; let prevName = communityRes.community_view.community.name;
await expect(createCommunity(alpha, prevName)).rejects.toBe( await expect(createCommunity(alpha, prevName)).rejects.toStrictEqual(
"community_already_exists", Error("community_already_exists"),
); );
// Cache the community on beta, make sure it has the other fields // Cache the community on beta, make sure it has the other fields

View file

@ -14,12 +14,10 @@ import {
betaUrl, betaUrl,
} from "./shared"; } from "./shared";
beforeAll(async () => { beforeAll(setupLogins);
await setupLogins();
});
afterAll(async () => { afterAll(() => {
await unfollowRemotes(alpha); unfollowRemotes(alpha);
}); });
test("Follow local community", async () => { test("Follow local community", async () => {

View file

@ -0,0 +1,44 @@
jest.setTimeout(120000);
import { UploadImage, DeleteImage } from "lemmy-js-client";
import { alpha, setupLogins, unfollowRemotes } from "./shared";
import fs = require("fs");
const downloadFileSync = require("download-file-sync");
beforeAll(setupLogins);
afterAll(() => {
unfollowRemotes(alpha);
});
test("Upload image and delete it", async () => {
// upload test image
const upload_image = fs.readFileSync("test.png");
const upload_form: UploadImage = {
image: upload_image,
};
const upload = await alpha.uploadImage(upload_form);
console.log(upload);
expect(upload.files![0].file).toBeDefined();
expect(upload.files![0].delete_token).toBeDefined();
expect(upload.url).toBeDefined();
expect(upload.delete_url).toBeDefined();
// ensure that image download is working. theres probably a better way to do this
const content = downloadFileSync(upload.url);
expect(content.length).toBeGreaterThan(0);
// delete image
const delete_form: DeleteImage = {
token: upload.files![0].delete_token,
filename: upload.files![0].file,
};
const delete_ = await alpha.deleteImage(delete_form);
expect(delete_).toBe(true);
// ensure that image is deleted
const content2 = downloadFileSync(upload.url);
expect(content2).toBe("");
});
// TODO: add tests for image purging

View file

@ -50,8 +50,8 @@ beforeAll(async () => {
await unfollows(); await unfollows();
}); });
afterAll(async () => { afterAll(() => {
await unfollows(); unfollows();
}); });
function assertPostFederation(postOne?: PostView, postTwo?: PostView) { function assertPostFederation(postOne?: PostView, postTwo?: PostView) {
@ -96,18 +96,20 @@ test("Create a post", async () => {
assertPostFederation(betaPost, postRes.post_view); assertPostFederation(betaPost, postRes.post_view);
// Delta only follows beta, so it should not see an alpha ap_id // Delta only follows beta, so it should not see an alpha ap_id
await expect(resolvePost(delta, postRes.post_view.post)).rejects.toBe( await expect(
"couldnt_find_object", resolvePost(delta, postRes.post_view.post),
); ).rejects.toStrictEqual(Error("couldnt_find_object"));
// Epsilon has alpha blocked, it should not see the alpha post // Epsilon has alpha blocked, it should not see the alpha post
await expect(resolvePost(epsilon, postRes.post_view.post)).rejects.toBe( await expect(
"couldnt_find_object", resolvePost(epsilon, postRes.post_view.post),
); ).rejects.toStrictEqual(Error("couldnt_find_object"));
}); });
test("Create a post in a non-existent community", async () => { test("Create a post in a non-existent community", async () => {
await expect(createPost(alpha, -2)).rejects.toBe("couldnt_find_community"); await expect(createPost(alpha, -2)).rejects.toStrictEqual(
Error("couldnt_find_community"),
);
}); });
test("Unlike a post", async () => { test("Unlike a post", async () => {
@ -157,8 +159,8 @@ test("Update a post", async () => {
assertPostFederation(betaPost, updatedPost.post_view); assertPostFederation(betaPost, updatedPost.post_view);
// Make sure lemmy beta cannot update the post // Make sure lemmy beta cannot update the post
await expect(editPost(beta, betaPost.post)).rejects.toBe( await expect(editPost(beta, betaPost.post)).rejects.toStrictEqual(
"no_post_edit_allowed", Error("no_post_edit_allowed"),
); );
}); });
@ -226,7 +228,9 @@ test("Lock a post", async () => {
); );
// Try to make a new comment there, on alpha // Try to make a new comment there, on alpha
await expect(createComment(alpha, alphaPost1.post.id)).rejects.toBe("locked"); await expect(createComment(alpha, alphaPost1.post.id)).rejects.toStrictEqual(
Error("locked"),
);
// Unlock a post // Unlock a post
let unlockedPost = await lockPost(beta, false, betaPost1.post); let unlockedPost = await lockPost(beta, false, betaPost1.post);
@ -281,8 +285,8 @@ test("Delete a post", async () => {
assertPostFederation(betaPost2, undeletedPost.post_view); assertPostFederation(betaPost2, undeletedPost.post_view);
// Make sure lemmy beta cannot delete the post // Make sure lemmy beta cannot delete the post
await expect(deletePost(beta, true, betaPost2.post)).rejects.toBe( await expect(deletePost(beta, true, betaPost2.post)).rejects.toStrictEqual(
"no_post_edit_allowed", Error("no_post_edit_allowed"),
); );
}); });
@ -483,12 +487,12 @@ test.skip("Enforce community ban for federated user", async () => {
// ensure that the post by alpha got removed // ensure that the post by alpha got removed
await expect(getPost(alpha, searchBeta1.posts[0].post.id)).rejects.toBe( await expect(getPost(alpha, searchBeta1.posts[0].post.id)).rejects.toBe(
"unknown", Error("unknown"),
); );
// Alpha tries to make post on beta, but it fails because of ban // Alpha tries to make post on beta, but it fails because of ban
await expect(createPost(alpha, betaCommunity.community.id)).rejects.toBe( await expect(createPost(alpha, betaCommunity.community.id)).rejects.toBe(
"banned_from_community", Error("banned_from_community"),
); );
// Unban alpha // Unban alpha

View file

@ -20,8 +20,8 @@ beforeAll(async () => {
recipient_id = 3; recipient_id = 3;
}); });
afterAll(async () => { afterAll(() => {
await unfollowRemotes(alpha); unfollowRemotes(alpha);
}); });
test("Create a private message", async () => { test("Create a private message", async () => {

View file

@ -10,6 +10,7 @@ import {
InstanceId, InstanceId,
LemmyHttp, LemmyHttp,
PostView, PostView,
SuccessResponse,
} from "lemmy-js-client"; } from "lemmy-js-client";
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost"; import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost"; import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
@ -58,7 +59,6 @@ import { Register } from "lemmy-js-client/dist/types/Register";
import { SaveUserSettings } from "lemmy-js-client/dist/types/SaveUserSettings"; import { SaveUserSettings } from "lemmy-js-client/dist/types/SaveUserSettings";
import { DeleteAccount } from "lemmy-js-client/dist/types/DeleteAccount"; import { DeleteAccount } from "lemmy-js-client/dist/types/DeleteAccount";
import { GetSiteResponse } from "lemmy-js-client/dist/types/GetSiteResponse"; import { GetSiteResponse } from "lemmy-js-client/dist/types/GetSiteResponse";
import { DeleteAccountResponse } from "lemmy-js-client/dist/types/DeleteAccountResponse";
import { PrivateMessagesResponse } from "lemmy-js-client/dist/types/PrivateMessagesResponse"; import { PrivateMessagesResponse } from "lemmy-js-client/dist/types/PrivateMessagesResponse";
import { GetPrivateMessages } from "lemmy-js-client/dist/types/GetPrivateMessages"; import { GetPrivateMessages } from "lemmy-js-client/dist/types/GetPrivateMessages";
import { PostReportResponse } from "lemmy-js-client/dist/types/PostReportResponse"; import { PostReportResponse } from "lemmy-js-client/dist/types/PostReportResponse";
@ -638,7 +638,7 @@ export async function loginUser(
export async function saveUserSettingsBio( export async function saveUserSettingsBio(
api: LemmyHttp, api: LemmyHttp,
): Promise<LoginResponse> { ): Promise<SuccessResponse> {
let form: SaveUserSettings = { let form: SaveUserSettings = {
show_nsfw: true, show_nsfw: true,
blur_nsfw: false, blur_nsfw: false,
@ -656,7 +656,7 @@ export async function saveUserSettingsBio(
export async function saveUserSettingsFederated( export async function saveUserSettingsFederated(
api: LemmyHttp, api: LemmyHttp,
): Promise<LoginResponse> { ): Promise<SuccessResponse> {
let avatar = "https://image.flaticon.com/icons/png/512/35/35896.png"; 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 banner = "https://image.flaticon.com/icons/png/512/36/35896.png";
let bio = "a changed bio"; let bio = "a changed bio";
@ -680,7 +680,7 @@ export async function saveUserSettingsFederated(
export async function saveUserSettings( export async function saveUserSettings(
api: LemmyHttp, api: LemmyHttp,
form: SaveUserSettings, form: SaveUserSettings,
): Promise<LoginResponse> { ): Promise<SuccessResponse> {
return api.saveUserSettings(form); return api.saveUserSettings(form);
} }
export async function getPersonDetails( export async function getPersonDetails(
@ -693,9 +693,7 @@ export async function getPersonDetails(
return api.getPersonDetails(form); return api.getPersonDetails(form);
} }
export async function deleteUser( export async function deleteUser(api: LemmyHttp): Promise<SuccessResponse> {
api: LemmyHttp,
): Promise<DeleteAccountResponse> {
let form: DeleteAccount = { let form: DeleteAccount = {
delete_content: true, delete_content: true,
password, password,

View file

@ -12,19 +12,17 @@ import {
createComment, createComment,
resolveBetaCommunity, resolveBetaCommunity,
deleteUser, deleteUser,
resolvePost,
resolveComment,
saveUserSettingsFederated, saveUserSettingsFederated,
setupLogins, setupLogins,
alphaUrl, alphaUrl,
saveUserSettings, saveUserSettings,
getPost,
getComments,
} from "./shared"; } from "./shared";
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client"; import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts"; import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
beforeAll(async () => { beforeAll(setupLogins);
await setupLogins();
});
let apShortname: string; let apShortname: string;
@ -103,18 +101,22 @@ test("Delete user", async () => {
await deleteUser(user); await deleteUser(user);
await expect(resolvePost(alpha, localPost)).rejects.toBe( // check that posts and comments are marked as deleted on other instances.
"couldnt_find_object", // use get methods to avoid refetching from origin instance
expect((await getPost(alpha, localPost.id)).post_view.post.deleted).toBe(
true,
); );
await expect(resolveComment(alpha, localComment)).rejects.toBe( expect((await getPost(alpha, remotePost.id)).post_view.post.deleted).toBe(
"couldnt_find_object", true,
);
await expect(resolvePost(alpha, remotePost)).rejects.toBe(
"couldnt_find_object",
);
await expect(resolveComment(alpha, remoteComment)).rejects.toBe(
"couldnt_find_object",
); );
expect(
(await getComments(alpha, localComment.post_id)).comments[0].comment
.deleted,
).toBe(true);
expect(
(await getComments(alpha, remoteComment.post_id)).comments[0].comment
.deleted,
).toBe(true);
}); });
test("Requests with invalid auth should be treated as unauthenticated", async () => { test("Requests with invalid auth should be treated as unauthenticated", async () => {

BIN
api_tests/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

3147
api_tests/yarn-error.log Normal file

File diff suppressed because it is too large Load diff

View file

@ -1270,6 +1270,11 @@ doctrine@^3.0.0:
dependencies: dependencies:
esutils "^2.0.2" esutils "^2.0.2"
download-file-sync@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/download-file-sync/-/download-file-sync-1.0.4.tgz#d3e3c543f836f41039455b9034c72e355b036019"
integrity sha512-vH92qNH508jZZA12HQNq/aiMDfagr4JvjFiI17Bi8oYjsxwv5ZVIi7iHkYmUXxOQUr90tcVX+8EPePjAqG1Y0w==
electron-to-chromium@^1.4.535: electron-to-chromium@^1.4.535:
version "1.4.537" version "1.4.537"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz#aac4101db53066be1e49baedd000a26bc754adc9" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz#aac4101db53066be1e49baedd000a26bc754adc9"
@ -2281,10 +2286,10 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
lemmy-js-client@0.19.0-rc.12: lemmy-js-client@0.19.0-alpha.18:
version "0.19.0-rc.12" version "0.19.0-alpha.18"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.12.tgz#e3bd4e21b1966d583ab790ef70ece8394b012b48" resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-alpha.18.tgz#f94841681cabdf9d5c4ce7048eacb57557f68724"
integrity sha512-1iu2fW9vlb3TrI+QR/ODP3+5pWZB0rUqL1wH09IzomDXohCqoQvfmXpwArmgF4Eq8GZgjkcfeMDC2gMrfw/i7Q== integrity sha512-cKJfKKnjK+ijk0Yd6ydtne3Y4FILp2RbQg05pCru9n6PCyPAa85eQL4QxPB1PPed20ckSZRcHLcnr/bYFDgpaw==
dependencies: dependencies:
cross-fetch "^3.1.5" cross-fetch "^3.1.5"
form-data "^4.0.0" form-data "^4.0.0"

View file

@ -52,6 +52,8 @@
# hotlinking is allowed. If that is the case for your instance, make sure that this setting is # hotlinking is allowed. If that is the case for your instance, make sure that this setting is
# disabled. # disabled.
cache_external_link_previews: true cache_external_link_previews: true
# Timeout for uploading images to pictrs (in seconds)
upload_timeout: 30
} }
# Email sending configuration. All options except login/password are mandatory # Email sending configuration. All options except login/password are mandatory
email: { email: {

View file

@ -1,4 +1,4 @@
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{GetReportCount, GetReportCountResponse}, person::{GetReportCount, GetReportCountResponse},
@ -14,7 +14,7 @@ use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn report_count( pub async fn report_count(
data: Json<GetReportCount>, data: Query<GetReportCount>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> Result<Json<GetReportCountResponse>, LemmyError> { ) -> Result<Json<GetReportCountResponse>, LemmyError> {

View file

@ -56,7 +56,6 @@ webpage = { version = "1.6", default-features = false, features = [
"serde", "serde",
], optional = true } ], optional = true }
encoding = { version = "0.2.33", optional = true } encoding = { version = "0.2.33", optional = true }
anyhow = { workspace = true }
futures = { workspace = true, optional = true } futures = { workspace = true, optional = true }
uuid = { workspace = true, optional = true } uuid = { workspace = true, optional = true }
tokio = { workspace = true, optional = true } tokio = { workspace = true, optional = true }

View file

@ -3,7 +3,6 @@ use crate::{
request::purge_image_from_pictrs, request::purge_image_from_pictrs,
site::{FederatedInstances, InstanceWithFederationState}, site::{FederatedInstances, InstanceWithFederationState},
}; };
use anyhow::Context;
use chrono::{DateTime, Days, Local, TimeZone, Utc}; use chrono::{DateTime, Days, Local, TimeZone, Utc};
use enum_map::{enum_map, EnumMap}; use enum_map::{enum_map, EnumMap};
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -34,7 +33,6 @@ use lemmy_db_views_actor::structs::{
use lemmy_utils::{ use lemmy_utils::{
email::{send_email, translations::Lang}, email::{send_email, translations::Lang},
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
location_info,
rate_limit::{ActionType, BucketConfig}, rate_limit::{ActionType, BucketConfig},
settings::structs::Settings, settings::structs::Settings,
utils::slurs::build_slur_regex, utils::slurs::build_slur_regex,
@ -786,24 +784,8 @@ pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
Ok(Url::parse(&format!("{actor_id}/inbox"))?.into()) Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
} }
pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> { pub fn generate_shared_inbox_url(settings: &Settings) -> Result<DbUrl, LemmyError> {
let mut actor_id: Url = actor_id.clone().into(); let url = format!("{}/inbox", settings.get_protocol_and_hostname());
actor_id.set_path("site_inbox");
Ok(actor_id.into())
}
pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
let actor_id: Url = actor_id.clone().into();
let url = format!(
"{}://{}{}/inbox",
&actor_id.scheme(),
&actor_id.host_str().context(location_info!())?,
if let Some(port) = actor_id.port() {
format!(":{port}")
} else {
String::new()
},
);
Ok(Url::parse(&url)?.into()) Ok(Url::parse(&url)?.into())
} }

View file

@ -90,7 +90,7 @@ pub async fn create_community(
.public_key(keypair.public_key) .public_key(keypair.public_key)
.followers_url(Some(generate_followers_url(&community_actor_id)?)) .followers_url(Some(generate_followers_url(&community_actor_id)?))
.inbox_url(Some(generate_inbox_url(&community_actor_id)?)) .inbox_url(Some(generate_inbox_url(&community_actor_id)?))
.shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?)) .shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
.posting_restricted_to_mods(data.posting_restricted_to_mods) .posting_restricted_to_mods(data.posting_restricted_to_mods)
.instance_id(site_view.site.instance_id) .instance_id(site_view.site.instance_id)
.build(); .build();

View file

@ -4,7 +4,7 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{CreateSite, SiteResponse}, site::{CreateSite, SiteResponse},
utils::{generate_site_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config}, utils::{generate_shared_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
@ -47,7 +47,7 @@ pub async fn create_site(
validate_create_payload(&local_site, &data)?; validate_create_payload(&local_site, &data)?;
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into(); let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
let inbox_url = Some(generate_site_inbox_url(&actor_id)?); let inbox_url = Some(generate_shared_inbox_url(context.settings())?);
let keypair = generate_actor_keypair()?; let keypair = generate_actor_keypair()?;
let site_form = SiteUpdateForm { let site_form = SiteUpdateForm {

View file

@ -113,7 +113,7 @@ pub async fn register(
.private_key(Some(actor_keypair.private_key)) .private_key(Some(actor_keypair.private_key))
.public_key(actor_keypair.public_key) .public_key(actor_keypair.public_key)
.inbox_url(Some(generate_inbox_url(&actor_id)?)) .inbox_url(Some(generate_inbox_url(&actor_id)?))
.shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?)) .shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
.instance_id(site_view.site.instance_id) .instance_id(site_view.site.instance_id)
.build(); .build();

View file

@ -66,7 +66,14 @@ impl ActivityHandler for Delete {
) )
.await .await
} else { } else {
receive_delete_action(self.object.id(), &self.actor, true, context).await receive_delete_action(
self.object.id(),
&self.actor,
true,
self.remove_data,
context,
)
.await
} }
} }
} }
@ -94,6 +101,7 @@ impl Delete {
summary, summary,
id, id,
audience: community.map(|c| c.actor_id.clone().into()), audience: community.map(|c| c.actor_id.clone().into()),
remove_data: None,
}) })
} }
} }
@ -164,6 +172,7 @@ pub(in crate::activities) async fn receive_remove_action(
.await?; .await?;
} }
DeletableObjects::PrivateMessage(_) => unimplemented!(), DeletableObjects::PrivateMessage(_) => unimplemented!(),
DeletableObjects::Person { .. } => unimplemented!(),
} }
Ok(()) Ok(())
} }

View file

@ -1,77 +0,0 @@
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
insert_received_activity,
objects::person::ApubPerson,
protocol::activities::deletion::delete_user::DeleteUser,
};
use activitypub_federation::{
config::Data,
kinds::{activity::DeleteType, public},
protocol::verification::verify_urls_match,
traits::{ActivityHandler, Actor},
};
use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
use lemmy_db_schema::source::{activity::ActivitySendTargets, person::Person};
use lemmy_utils::error::LemmyError;
use url::Url;
pub async fn delete_user(
person: Person,
delete_content: bool,
context: Data<LemmyContext>,
) -> Result<(), LemmyError> {
let actor: ApubPerson = person.into();
let id = generate_activity_id(
DeleteType::Delete,
&context.settings().get_protocol_and_hostname(),
)?;
let delete = DeleteUser {
actor: actor.id().into(),
to: vec![public()],
object: actor.id().into(),
kind: DeleteType::Delete,
id: id.clone(),
cc: vec![],
remove_data: Some(delete_content),
};
let inboxes = ActivitySendTargets::to_all_instances();
send_lemmy_activity(&context, delete, &actor, inboxes, true).await?;
Ok(())
}
/// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
/// (cause instance actor doesn't have shared inbox).
#[async_trait::async_trait]
impl ActivityHandler for DeleteUser {
type DataType = LemmyContext;
type Error = LemmyError;
fn id(&self) -> &Url {
&self.id
}
fn actor(&self) -> &Url {
self.actor.inner()
}
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &[])?;
verify_person(&self.actor, context).await?;
verify_urls_match(self.actor.inner(), self.object.inner())?;
Ok(())
}
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
let actor = self.actor.dereference(context).await?;
if self.remove_data.unwrap_or(false) {
purge_user_account(actor.id, context).await?;
} else {
Person::delete_account(&mut context.pool(), actor.id).await?;
}
Ok(())
}
}

View file

@ -24,10 +24,10 @@ use activitypub_federation::{
config::Data, config::Data,
fetch::object_id::ObjectId, fetch::object_id::ObjectId,
kinds::public, kinds::public,
protocol::verification::verify_domains_match, protocol::verification::{verify_domains_match, verify_urls_match},
traits::{Actor, Object}, traits::{Actor, Object},
}; };
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::CommunityId, newtypes::CommunityId,
source::{ source::{
@ -45,7 +45,6 @@ use std::ops::Deref;
use url::Url; use url::Url;
pub mod delete; pub mod delete;
pub mod delete_user;
pub mod undo_delete; pub mod undo_delete;
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
@ -135,8 +134,26 @@ pub(crate) async fn send_apub_delete_private_message(
Ok(()) Ok(())
} }
pub async fn send_apub_delete_user(
person: Person,
remove_data: bool,
context: Data<LemmyContext>,
) -> Result<(), LemmyError> {
let person: ApubPerson = person.into();
let deletable = DeletableObjects::Person(person.clone());
let mut delete: Delete = Delete::new(&person, deletable, public(), None, None, &context)?;
delete.remove_data = Some(remove_data);
let inboxes = ActivitySendTargets::to_all_instances();
send_lemmy_activity(&context, delete, &person, inboxes, true).await?;
Ok(())
}
pub enum DeletableObjects { pub enum DeletableObjects {
Community(ApubCommunity), Community(ApubCommunity),
Person(ApubPerson),
Comment(ApubComment), Comment(ApubComment),
Post(ApubPost), Post(ApubPost),
PrivateMessage(ApubPrivateMessage), PrivateMessage(ApubPrivateMessage),
@ -151,6 +168,9 @@ impl DeletableObjects {
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? { if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Community(c)); return Ok(DeletableObjects::Community(c));
} }
if let Some(p) = ApubPerson::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Person(p));
}
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? { if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Post(p)); return Ok(DeletableObjects::Post(p));
} }
@ -166,6 +186,7 @@ impl DeletableObjects {
pub(crate) fn id(&self) -> Url { pub(crate) fn id(&self) -> Url {
match self { match self {
DeletableObjects::Community(c) => c.id(), DeletableObjects::Community(c) => c.id(),
DeletableObjects::Person(p) => p.id(),
DeletableObjects::Comment(c) => c.ap_id.clone().into(), DeletableObjects::Comment(c) => c.ap_id.clone().into(),
DeletableObjects::Post(p) => p.ap_id.clone().into(), DeletableObjects::Post(p) => p.ap_id.clone().into(),
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(), DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
@ -191,6 +212,11 @@ pub(in crate::activities) async fn verify_delete_activity(
// community deletion is always a mod (or admin) action // community deletion is always a mod (or admin) action
verify_mod_action(&activity.actor, &community, context).await?; verify_mod_action(&activity.actor, &community, context).await?;
} }
DeletableObjects::Person(person) => {
verify_is_public(&activity.to, &[])?;
verify_person(&activity.actor, context).await?;
verify_urls_match(person.actor_id.inner(), activity.object.id())?;
}
DeletableObjects::Post(p) => { DeletableObjects::Post(p) => {
verify_is_public(&activity.to, &[])?; verify_is_public(&activity.to, &[])?;
verify_delete_post_or_comment( verify_delete_post_or_comment(
@ -245,6 +271,7 @@ async fn receive_delete_action(
object: &Url, object: &Url,
actor: &ObjectId<ApubPerson>, actor: &ObjectId<ApubPerson>,
deleted: bool, deleted: bool,
do_purge_user_account: Option<bool>,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
@ -266,6 +293,13 @@ async fn receive_delete_action(
) )
.await?; .await?;
} }
DeletableObjects::Person(person) => {
if do_purge_user_account.unwrap_or(false) {
purge_user_account(person.id, context).await?;
} else {
Person::delete_account(&mut context.pool(), person.id).await?;
}
}
DeletableObjects::Post(post) => { DeletableObjects::Post(post) => {
if deleted != post.deleted { if deleted != post.deleted {
Post::update( Post::update(

View file

@ -58,7 +58,7 @@ impl ActivityHandler for UndoDelete {
) )
.await .await
} else { } else {
receive_delete_action(self.object.object.id(), &self.actor, false, context).await receive_delete_action(self.object.object.id(), &self.actor, false, None, context).await
} }
} }
} }
@ -156,6 +156,7 @@ impl UndoDelete {
.await?; .await?;
} }
DeletableObjects::PrivateMessage(_) => unimplemented!(), DeletableObjects::PrivateMessage(_) => unimplemented!(),
DeletableObjects::Person { .. } => unimplemented!(),
} }
Ok(()) Ok(())
} }

View file

@ -9,10 +9,10 @@ use crate::{
}, },
create_or_update::private_message::send_create_or_update_pm, create_or_update::private_message::send_create_or_update_pm,
deletion::{ deletion::{
delete_user::delete_user,
send_apub_delete_in_community, send_apub_delete_in_community,
send_apub_delete_in_community_new, send_apub_delete_in_community_new,
send_apub_delete_private_message, send_apub_delete_private_message,
send_apub_delete_user,
DeletableObjects, DeletableObjects,
}, },
voting::send_like_activity, voting::send_like_activity,
@ -330,7 +330,7 @@ pub async fn match_outgoing_activities(
DeletePrivateMessage(person, pm, deleted) => { DeletePrivateMessage(person, pm, deleted) => {
send_apub_delete_private_message(&person.into(), pm, deleted, context).await send_apub_delete_private_message(&person.into(), pm, deleted, context).await
} }
DeleteUser(person, delete_content) => delete_user(person, delete_content, context).await, DeleteUser(person, remove_data) => send_apub_delete_user(person, remove_data, context).await,
CreateReport(url, actor, community, reason) => { CreateReport(url, actor, community, reason) => {
Report::send(ObjectId::from(url), actor, community, reason, context).await Report::send(ObjectId::from(url), actor, community, reason, context).await
} }

View file

@ -16,7 +16,7 @@ use crate::{
note::CreateOrUpdateNote, note::CreateOrUpdateNote,
page::CreateOrUpdatePage, page::CreateOrUpdatePage,
}, },
deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete}, deletion::{delete::Delete, undo_delete::UndoDelete},
following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow}, following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
voting::{undo_vote::UndoVote, vote::Vote}, voting::{undo_vote::UndoVote, vote::Vote},
}, },
@ -98,16 +98,6 @@ pub enum AnnouncableActivities {
Page(Page), Page(Page),
} }
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
#[enum_delegate::implement(ActivityHandler)]
#[allow(clippy::enum_variant_names)]
pub enum SiteInboxActivities {
BlockUser(BlockUser),
UndoBlockUser(UndoBlockUser),
DeleteUser(DeleteUser),
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl InCommunity for AnnouncableActivities { impl InCommunity for AnnouncableActivities {
#[tracing::instrument(skip(self, context))] #[tracing::instrument(skip(self, context))]
@ -134,44 +124,43 @@ impl InCommunity for AnnouncableActivities {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use crate::{ use crate::{
activity_lists::{GroupInboxActivities, PersonInboxActivities, SiteInboxActivities}, activity_lists::{GroupInboxActivities, PersonInboxActivities, SharedInboxActivities},
protocol::tests::{test_json, test_parse_lemmy_item}, protocol::tests::{test_json, test_parse_lemmy_item},
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_group_inbox() { fn test_group_inbox() -> LemmyResult<()> {
test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json") test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json")?;
.unwrap();
test_parse_lemmy_item::<GroupInboxActivities>( test_parse_lemmy_item::<GroupInboxActivities>(
"assets/lemmy/activities/create_or_update/create_note.json", "assets/lemmy/activities/create_or_update/create_note.json",
) )?;
.unwrap(); Ok(())
} }
#[test] #[test]
fn test_person_inbox() { fn test_person_inbox() -> LemmyResult<()> {
test_parse_lemmy_item::<PersonInboxActivities>("assets/lemmy/activities/following/accept.json") test_parse_lemmy_item::<PersonInboxActivities>(
.unwrap(); "assets/lemmy/activities/following/accept.json",
)?;
test_parse_lemmy_item::<PersonInboxActivities>( test_parse_lemmy_item::<PersonInboxActivities>(
"assets/lemmy/activities/create_or_update/create_note.json", "assets/lemmy/activities/create_or_update/create_note.json",
) )?;
.unwrap();
test_parse_lemmy_item::<PersonInboxActivities>( test_parse_lemmy_item::<PersonInboxActivities>(
"assets/lemmy/activities/create_or_update/create_private_message.json", "assets/lemmy/activities/create_or_update/create_private_message.json",
) )?;
.unwrap(); test_json::<PersonInboxActivities>("assets/mastodon/activities/follow.json")?;
test_json::<PersonInboxActivities>("assets/mastodon/activities/follow.json").unwrap(); Ok(())
} }
#[test] #[test]
fn test_site_inbox() { fn test_shared_inbox() -> LemmyResult<()> {
test_parse_lemmy_item::<SiteInboxActivities>( test_parse_lemmy_item::<SharedInboxActivities>(
"assets/lemmy/activities/deletion/delete_user.json", "assets/lemmy/activities/deletion/delete_user.json",
) )?;
.unwrap(); Ok(())
} }
} }

View file

@ -277,7 +277,6 @@ pub async fn import_settings(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use crate::{ use crate::{
@ -297,7 +296,7 @@ mod tests {
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityFollowerView; use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::error::LemmyErrorType; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serial_test::serial; use serial_test::serial;
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep; use tokio::time::sleep;
@ -306,10 +305,8 @@ mod tests {
name: String, name: String,
bio: Option<String>, bio: Option<String>,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LocalUserView { ) -> LemmyResult<LocalUserView> {
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()) let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
.await
.unwrap();
let person_form = PersonInsertForm::builder() let person_form = PersonInsertForm::builder()
.name(name.clone()) .name(name.clone())
.display_name(Some(name.clone())) .display_name(Some(name.clone()))
@ -317,63 +314,49 @@ mod tests {
.public_key("asd".to_string()) .public_key("asd".to_string())
.instance_id(instance.id) .instance_id(instance.id)
.build(); .build();
let person = Person::create(&mut context.pool(), &person_form) let person = Person::create(&mut context.pool(), &person_form).await?;
.await
.unwrap();
let user_form = LocalUserInsertForm::builder() let user_form = LocalUserInsertForm::builder()
.person_id(person.id) .person_id(person.id)
.password_encrypted("pass".to_string()) .password_encrypted("pass".to_string())
.build(); .build();
let local_user = LocalUser::create(&mut context.pool(), &user_form) let local_user = LocalUser::create(&mut context.pool(), &user_form).await?;
.await
.unwrap();
LocalUserView::read(&mut context.pool(), local_user.id) Ok(LocalUserView::read(&mut context.pool(), local_user.id).await?)
.await
.unwrap()
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_settings_export_import() { async fn test_settings_export_import() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let export_user = create_user("hanna".to_string(), Some("my bio".to_string()), &context).await; let export_user =
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
let community_form = CommunityInsertForm::builder() let community_form = CommunityInsertForm::builder()
.name("testcom".to_string()) .name("testcom".to_string())
.title("testcom".to_string()) .title("testcom".to_string())
.instance_id(export_user.person.instance_id) .instance_id(export_user.person.instance_id)
.build(); .build();
let community = Community::create(&mut context.pool(), &community_form) let community = Community::create(&mut context.pool(), &community_form).await?;
.await
.unwrap();
let follower_form = CommunityFollowerForm { let follower_form = CommunityFollowerForm {
community_id: community.id, community_id: community.id,
person_id: export_user.person.id, person_id: export_user.person.id,
pending: false, pending: false,
}; };
CommunityFollower::follow(&mut context.pool(), &follower_form) CommunityFollower::follow(&mut context.pool(), &follower_form).await?;
.await
.unwrap();
let backup = export_settings(export_user.clone(), context.reset_request_count()) let backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
.await
.unwrap();
let import_user = create_user("charles".to_string(), None, &context).await; let import_user = create_user("charles".to_string(), None, &context).await?;
import_settings(backup, import_user.clone(), context.reset_request_count()) import_settings(backup, import_user.clone(), context.reset_request_count()).await?;
.await
.unwrap();
// wait for background task to finish // wait for background task to finish
sleep(Duration::from_millis(1000)).await; sleep(Duration::from_millis(1000)).await;
let import_user_updated = LocalUserView::read(&mut context.pool(), import_user.local_user.id) let import_user_updated =
.await LocalUserView::read(&mut context.pool(), import_user.local_user.id).await?;
.unwrap();
assert_eq!( assert_eq!(
export_user.person.display_name, export_user.person.display_name,
@ -381,61 +364,49 @@ mod tests {
); );
assert_eq!(export_user.person.bio, import_user_updated.person.bio); assert_eq!(export_user.person.bio, import_user_updated.person.bio);
let follows = CommunityFollowerView::for_person(&mut context.pool(), import_user.person.id) let follows =
.await CommunityFollowerView::for_person(&mut context.pool(), import_user.person.id).await?;
.unwrap();
assert_eq!(follows.len(), 1); assert_eq!(follows.len(), 1);
assert_eq!(follows[0].community.actor_id, community.actor_id); assert_eq!(follows[0].community.actor_id, community.actor_id);
LocalUser::delete(&mut context.pool(), export_user.local_user.id) LocalUser::delete(&mut context.pool(), export_user.local_user.id).await?;
.await LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
.unwrap(); Ok(())
LocalUser::delete(&mut context.pool(), import_user.local_user.id)
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn disallow_large_backup() { async fn disallow_large_backup() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let export_user = create_user("hanna".to_string(), Some("my bio".to_string()), &context).await; let export_user =
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
let mut backup = export_settings(export_user.clone(), context.reset_request_count()) let mut backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
.await
.unwrap();
for _ in 0..251 { for _ in 0..251 {
backup backup
.followed_communities .followed_communities
.push("http://example.com".parse().unwrap()); .push("http://example.com".parse()?);
backup backup
.blocked_communities .blocked_communities
.push("http://example2.com".parse().unwrap()); .push("http://example2.com".parse()?);
backup backup.saved_posts.push("http://example3.com".parse()?);
.saved_posts backup.saved_comments.push("http://example4.com".parse()?);
.push("http://example3.com".parse().unwrap());
backup
.saved_comments
.push("http://example4.com".parse().unwrap());
} }
let import_user = create_user("charles".to_string(), None, &context).await; let import_user = create_user("charles".to_string(), None, &context).await?;
let imported = let imported =
import_settings(backup, import_user.clone(), context.reset_request_count()).await; import_settings(backup, import_user.clone(), context.reset_request_count()).await;
assert_eq!( assert_eq!(
imported.err().unwrap().error_type, imported.err().map(|e| e.error_type),
LemmyErrorType::TooManyItems Some(LemmyErrorType::TooManyItems)
); );
LocalUser::delete(&mut context.pool(), export_user.local_user.id) LocalUser::delete(&mut context.pool(), export_user.local_user.id).await?;
.await LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
.unwrap(); Ok(())
LocalUser::delete(&mut context.pool(), import_user.local_user.id)
.await
.unwrap();
} }
} }

View file

@ -102,7 +102,6 @@ impl Collection for ApubCommunityModerators {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use super::*; use super::*;
@ -123,20 +122,19 @@ mod tests {
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_community_moderators() { async fn test_parse_lemmy_community_moderators() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let (new_mod, site) = parse_lemmy_person(&context).await; let (new_mod, site) = parse_lemmy_person(&context).await?;
let community = parse_lemmy_community(&context).await; let community = parse_lemmy_community(&context).await?;
let community_id = community.id; let community_id = community.id;
let inserted_instance = let inserted_instance =
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()) Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;
.await
.unwrap();
let old_mod = PersonInsertForm::builder() let old_mod = PersonInsertForm::builder()
.name("holly".into()) .name("holly".into())
@ -144,49 +142,34 @@ mod tests {
.instance_id(inserted_instance.id) .instance_id(inserted_instance.id)
.build(); .build();
let old_mod = Person::create(&mut context.pool(), &old_mod).await.unwrap(); let old_mod = Person::create(&mut context.pool(), &old_mod).await?;
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: community.id, community_id: community.id,
person_id: old_mod.id, person_id: old_mod.id,
}; };
CommunityModerator::join(&mut context.pool(), &community_moderator_form) CommunityModerator::join(&mut context.pool(), &community_moderator_form).await?;
.await
.unwrap();
assert_eq!(site.actor_id.to_string(), "https://enterprise.lemmy.ml/"); assert_eq!(site.actor_id.to_string(), "https://enterprise.lemmy.ml/");
let json: GroupModerators = let json: GroupModerators =
file_to_json_object("assets/lemmy/collections/group_moderators.json").unwrap(); file_to_json_object("assets/lemmy/collections/group_moderators.json")?;
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward")?;
ApubCommunityModerators::verify(&json, &url, &context) ApubCommunityModerators::verify(&json, &url, &context).await?;
.await ApubCommunityModerators::from_json(json, &community, &context).await?;
.unwrap();
ApubCommunityModerators::from_json(json, &community, &context)
.await
.unwrap();
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
let current_moderators = let current_moderators =
CommunityModeratorView::for_community(&mut context.pool(), community_id) CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
.await
.unwrap();
assert_eq!(current_moderators.len(), 1); assert_eq!(current_moderators.len(), 1);
assert_eq!(current_moderators[0].moderator.id, new_mod.id); assert_eq!(current_moderators[0].moderator.id, new_mod.id);
Person::delete(&mut context.pool(), old_mod.id) Person::delete(&mut context.pool(), old_mod.id).await?;
.await Person::delete(&mut context.pool(), new_mod.id).await?;
.unwrap(); Community::delete(&mut context.pool(), community.id).await?;
Person::delete(&mut context.pool(), new_mod.id) Site::delete(&mut context.pool(), site.id).await?;
.await Instance::delete(&mut context.pool(), inserted_instance.id).await?;
.unwrap(); Ok(())
Community::delete(&mut context.pool(), community.id)
.await
.unwrap();
Site::delete(&mut context.pool(), site.id).await.unwrap();
Instance::delete(&mut context.pool(), inserted_instance.id)
.await
.unwrap();
} }
} }

View file

@ -12,7 +12,7 @@ use crate::http::{
person::{get_apub_person_http, get_apub_person_outbox, person_inbox}, person::{get_apub_person_http, get_apub_person_outbox, person_inbox},
post::get_apub_post, post::get_apub_post,
shared_inbox, shared_inbox,
site::{get_apub_site_http, get_apub_site_inbox, get_apub_site_outbox}, site::{get_apub_site_http, get_apub_site_outbox},
}; };
use actix_web::{ use actix_web::{
guard::{Guard, GuardContext}, guard::{Guard, GuardContext},
@ -58,8 +58,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.guard(InboxRequestGuard) .guard(InboxRequestGuard)
.route("/c/{community_name}/inbox", web::post().to(community_inbox)) .route("/c/{community_name}/inbox", web::post().to(community_inbox))
.route("/u/{user_name}/inbox", web::post().to(person_inbox)) .route("/u/{user_name}/inbox", web::post().to(person_inbox))
.route("/inbox", web::post().to(shared_inbox)) .route("/inbox", web::post().to(shared_inbox)),
.route("/site_inbox", web::post().to(get_apub_site_inbox)),
); );
} }

View file

@ -1,16 +1,10 @@
use crate::{ use crate::{
activity_lists::SiteInboxActivities,
http::create_apub_response, http::create_apub_response,
objects::{instance::ApubSite, person::ApubPerson}, objects::instance::ApubSite,
protocol::collections::empty_outbox::EmptyOutbox, protocol::collections::empty_outbox::EmptyOutbox,
}; };
use activitypub_federation::{ use activitypub_federation::{config::Data, traits::Object};
actix_web::inbox::receive_activity, use actix_web::HttpResponse;
config::Data,
protocol::context::WithContext,
traits::Object,
};
use actix_web::{web::Bytes, HttpRequest, HttpResponse};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
@ -36,15 +30,3 @@ pub(crate) async fn get_apub_site_outbox(
let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?)?; let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?)?;
create_apub_response(&outbox) create_apub_response(&outbox)
} }
#[tracing::instrument(skip_all)]
pub async fn get_apub_site_inbox(
request: HttpRequest,
body: Bytes,
data: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
receive_activity::<WithContext<SiteInboxActivities>, ApubPerson, LemmyContext>(
request, body, &data,
)
.await
}

View file

@ -183,9 +183,6 @@ impl Object for ApubComment {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{ use crate::{
objects::{ objects::{
@ -200,46 +197,45 @@ pub(crate) mod tests {
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use html2md::parse_html; use html2md::parse_html;
use lemmy_db_schema::source::site::Site; use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
async fn prepare_comment_test( async fn prepare_comment_test(
url: &Url, url: &Url,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> (ApubPerson, ApubCommunity, ApubPost, ApubSite) { ) -> LemmyResult<(ApubPerson, ApubCommunity, ApubPost, ApubSite)> {
// use separate counter so this doesnt affect tests // use separate counter so this doesnt affect tests
let context2 = context.reset_request_count(); let context2 = context.reset_request_count();
let (person, site) = parse_lemmy_person(&context2).await; let (person, site) = parse_lemmy_person(&context2).await?;
let community = parse_lemmy_community(&context2).await; let community = parse_lemmy_community(&context2).await?;
let post_json = file_to_json_object("assets/lemmy/objects/page.json").unwrap(); let post_json = file_to_json_object("assets/lemmy/objects/page.json")?;
ApubPost::verify(&post_json, url, &context2).await.unwrap(); ApubPost::verify(&post_json, url, &context2).await?;
let post = ApubPost::from_json(post_json, &context2).await.unwrap(); let post = ApubPost::from_json(post_json, &context2).await?;
(person, community, post, site) Ok((person, community, post, site))
} }
async fn cleanup(data: (ApubPerson, ApubCommunity, ApubPost, ApubSite), context: &LemmyContext) { async fn cleanup(
Post::delete(&mut context.pool(), data.2.id).await.unwrap(); data: (ApubPerson, ApubCommunity, ApubPost, ApubSite),
Community::delete(&mut context.pool(), data.1.id) context: &LemmyContext,
.await ) -> LemmyResult<()> {
.unwrap(); Post::delete(&mut context.pool(), data.2.id).await?;
Person::delete(&mut context.pool(), data.0.id) Community::delete(&mut context.pool(), data.1.id).await?;
.await Person::delete(&mut context.pool(), data.0.id).await?;
.unwrap(); Site::delete(&mut context.pool(), data.3.id).await?;
Site::delete(&mut context.pool(), data.3.id).await.unwrap(); LocalSite::delete(&mut context.pool()).await?;
LocalSite::delete(&mut context.pool()).await.unwrap(); Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
pub(crate) async fn test_parse_lemmy_comment() { pub(crate) async fn test_parse_lemmy_comment() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
let data = prepare_comment_test(&url, &context).await; let data = prepare_comment_test(&url, &context).await?;
let json: Note = file_to_json_object("assets/lemmy/objects/note.json").unwrap(); let json: Note = file_to_json_object("assets/lemmy/objects/note.json")?;
ApubComment::verify(&json, &url, &context).await.unwrap(); ApubComment::verify(&json, &url, &context).await?;
let comment = ApubComment::from_json(json.clone(), &context) let comment = ApubComment::from_json(json.clone(), &context).await?;
.await
.unwrap();
assert_eq!(comment.ap_id, url.into()); assert_eq!(comment.ap_id, url.into());
assert_eq!(comment.content.len(), 14); assert_eq!(comment.content.len(), 14);
@ -247,45 +243,38 @@ pub(crate) mod tests {
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
let comment_id = comment.id; let comment_id = comment.id;
let to_apub = comment.into_json(&context).await.unwrap(); let to_apub = comment.into_json(&context).await?;
assert_json_include!(actual: json, expected: to_apub); assert_json_include!(actual: json, expected: to_apub);
Comment::delete(&mut context.pool(), comment_id) Comment::delete(&mut context.pool(), comment_id).await?;
.await cleanup(data, &context).await?;
.unwrap(); Ok(())
cleanup(data, &context).await;
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_pleroma_comment() { async fn test_parse_pleroma_comment() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
let data = prepare_comment_test(&url, &context).await; let data = prepare_comment_test(&url, &context).await?;
let pleroma_url = let pleroma_url =
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2") Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")?;
.unwrap(); let person_json = file_to_json_object("assets/pleroma/objects/person.json")?;
let person_json = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); ApubPerson::verify(&person_json, &pleroma_url, &context).await?;
ApubPerson::verify(&person_json, &pleroma_url, &context) ApubPerson::from_json(person_json, &context).await?;
.await let json = file_to_json_object("assets/pleroma/objects/note.json")?;
.unwrap(); ApubComment::verify(&json, &pleroma_url, &context).await?;
ApubPerson::from_json(person_json, &context).await.unwrap(); let comment = ApubComment::from_json(json, &context).await?;
let json = file_to_json_object("assets/pleroma/objects/note.json").unwrap();
ApubComment::verify(&json, &pleroma_url, &context)
.await
.unwrap();
let comment = ApubComment::from_json(json, &context).await.unwrap();
assert_eq!(comment.ap_id, pleroma_url.into()); assert_eq!(comment.ap_id, pleroma_url.into());
assert_eq!(comment.content.len(), 64); assert_eq!(comment.content.len(), 64);
assert!(!comment.local); assert!(!comment.local);
assert_eq!(context.request_count(), 1); assert_eq!(context.request_count(), 1);
Comment::delete(&mut context.pool(), comment.id) Comment::delete(&mut context.pool(), comment.id).await?;
.await cleanup(data, &context).await?;
.unwrap(); Ok(())
cleanup(data, &context).await;
} }
#[tokio::test] #[tokio::test]

View file

@ -216,9 +216,6 @@ impl ApubCommunity {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{ use crate::{
objects::{instance::tests::parse_lemmy_instance, tests::init_context}, objects::{instance::tests::parse_lemmy_instance, tests::init_context},
@ -226,41 +223,44 @@ pub(crate) mod tests {
}; };
use activitypub_federation::fetch::collection_id::CollectionId; use activitypub_federation::fetch::collection_id::CollectionId;
use lemmy_db_schema::{source::site::Site, traits::Crud}; use lemmy_db_schema::{source::site::Site, traits::Crud};
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
pub(crate) async fn parse_lemmy_community(context: &Data<LemmyContext>) -> ApubCommunity { pub(crate) async fn parse_lemmy_community(
context: &Data<LemmyContext>,
) -> LemmyResult<ApubCommunity> {
// use separate counter so this doesnt affect tests // use separate counter so this doesnt affect tests
let context2 = context.reset_request_count(); let context2 = context.reset_request_count();
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json").unwrap(); let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json")?;
// change these links so they dont fetch over the network // change these links so they dont fetch over the network
json.attributed_to = None; json.attributed_to = None;
json.outbox = json.outbox = CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox")?;
CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap(); json.followers = CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_followers")?;
json.followers =
CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_followers").unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward")?;
ApubCommunity::verify(&json, &url, &context2).await.unwrap(); ApubCommunity::verify(&json, &url, &context2).await?;
let community = ApubCommunity::from_json(json, &context2).await.unwrap(); let community = ApubCommunity::from_json(json, &context2).await?;
// this makes requests to the (intentionally broken) outbox and followers collections // this makes requests to the (intentionally broken) outbox and followers collections
assert_eq!(context2.request_count(), 2); assert_eq!(context2.request_count(), 2);
community Ok(community)
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_community() { async fn test_parse_lemmy_community() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let site = parse_lemmy_instance(&context).await; let site = parse_lemmy_instance(&context).await?;
let community = parse_lemmy_community(&context).await; let community = parse_lemmy_community(&context).await?;
assert_eq!(community.title, "Ten Forward"); assert_eq!(community.title, "Ten Forward");
assert!(!community.local); assert!(!community.local);
assert_eq!(community.description.as_ref().unwrap().len(), 132); assert_eq!(
community.description.as_ref().map(std::string::String::len),
Some(132)
);
Community::delete(&mut context.pool(), community.id) Community::delete(&mut context.pool(), community.id).await?;
.await Site::delete(&mut context.pool(), site.id).await?;
.unwrap(); Ok(())
Site::delete(&mut context.pool(), site.id).await.unwrap();
} }
} }

View file

@ -204,32 +204,34 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + C
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{objects::tests::init_context, protocol::tests::file_to_json_object}; use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
use lemmy_db_schema::traits::Crud; use lemmy_db_schema::traits::Crud;
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> ApubSite { pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> LemmyResult<ApubSite> {
let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap(); let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json")?;
let id = Url::parse("https://enterprise.lemmy.ml/").unwrap(); let id = Url::parse("https://enterprise.lemmy.ml/")?;
ApubSite::verify(&json, &id, context).await.unwrap(); ApubSite::verify(&json, &id, context).await?;
let site = ApubSite::from_json(json, context).await.unwrap(); let site = ApubSite::from_json(json, context).await?;
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
site Ok(site)
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_instance() { async fn test_parse_lemmy_instance() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let site = parse_lemmy_instance(&context).await; let site = parse_lemmy_instance(&context).await?;
assert_eq!(site.name, "Enterprise"); assert_eq!(site.name, "Enterprise");
assert_eq!(site.description.as_ref().unwrap().len(), 15); assert_eq!(
site.description.as_ref().map(std::string::String::len),
Some(15)
);
Site::delete(&mut context.pool(), site.id).await.unwrap(); Site::delete(&mut context.pool(), site.id).await?;
Ok(())
} }
} }

View file

@ -54,14 +54,11 @@ pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<(
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use activitypub_federation::config::{Data, FederationConfig}; use activitypub_federation::config::{Data, FederationConfig};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::{context::LemmyContext, request::client_builder}; use lemmy_api_common::{context::LemmyContext, request::client_builder};
use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests}; use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
use lemmy_utils::{rate_limit::RateLimitCell, settings::SETTINGS}; use lemmy_utils::{error::LemmyResult, rate_limit::RateLimitCell, settings::SETTINGS};
use reqwest::{Request, Response}; use reqwest::{Request, Response};
use reqwest_middleware::{ClientBuilder, Middleware, Next}; use reqwest_middleware::{ClientBuilder, Middleware, Next};
use task_local_extensions::Extensions; use task_local_extensions::Extensions;
@ -82,11 +79,11 @@ pub(crate) mod tests {
} }
// TODO: would be nice if we didnt have to use a full context for tests. // TODO: would be nice if we didnt have to use a full context for tests.
pub(crate) async fn init_context() -> Data<LemmyContext> { pub(crate) async fn init_context() -> LemmyResult<Data<LemmyContext>> {
// call this to run migrations // call this to run migrations
let pool = build_db_pool_for_tests().await; let pool = build_db_pool_for_tests().await;
let client = client_builder(&SETTINGS).build().unwrap(); let client = client_builder(&SETTINGS).build()?;
let client = ClientBuilder::new(client).with(BlockedMiddleware).build(); let client = ClientBuilder::new(client).with(BlockedMiddleware).build();
let secret = Secret { let secret = Secret {
@ -101,8 +98,7 @@ pub(crate) mod tests {
.domain("example.com") .domain("example.com")
.app_data(context) .app_data(context)
.build() .build()
.await .await?;
.unwrap(); Ok(config.to_request_data())
config.to_request_data()
} }
} }

View file

@ -208,9 +208,6 @@ impl GetActorType for ApubPerson {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{ use crate::{
objects::{ objects::{
@ -221,60 +218,64 @@ pub(crate) mod tests {
}; };
use activitypub_federation::fetch::object_id::ObjectId; use activitypub_federation::fetch::object_id::ObjectId;
use lemmy_db_schema::{source::site::Site, traits::Crud}; use lemmy_db_schema::{source::site::Site, traits::Crud};
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
pub(crate) async fn parse_lemmy_person(context: &Data<LemmyContext>) -> (ApubPerson, ApubSite) { pub(crate) async fn parse_lemmy_person(
let site = parse_lemmy_instance(context).await; context: &Data<LemmyContext>,
let json = file_to_json_object("assets/lemmy/objects/person.json").unwrap(); ) -> LemmyResult<(ApubPerson, ApubSite)> {
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap(); let site = parse_lemmy_instance(context).await?;
ApubPerson::verify(&json, &url, context).await.unwrap(); let json = file_to_json_object("assets/lemmy/objects/person.json")?;
let person = ApubPerson::from_json(json, context).await.unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/u/picard")?;
ApubPerson::verify(&json, &url, context).await?;
let person = ApubPerson::from_json(json, context).await?;
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
(person, site) Ok((person, site))
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_person() { async fn test_parse_lemmy_person() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let (person, site) = parse_lemmy_person(&context).await; let (person, site) = parse_lemmy_person(&context).await?;
assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string())); assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string()));
assert!(!person.local); assert!(!person.local);
assert_eq!(person.bio.as_ref().unwrap().len(), 39); assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(39));
cleanup((person, site), &context).await; cleanup((person, site), &context).await?;
Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_pleroma_person() { async fn test_parse_pleroma_person() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
// create and parse a fake pleroma instance actor, to avoid network request during test // create and parse a fake pleroma instance actor, to avoid network request during test
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap(); let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json")?;
json.id = ObjectId::parse("https://queer.hacktivis.me/").unwrap(); json.id = ObjectId::parse("https://queer.hacktivis.me/")?;
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); let url = Url::parse("https://queer.hacktivis.me/users/lanodan")?;
ApubSite::verify(&json, &url, &context).await.unwrap(); ApubSite::verify(&json, &url, &context).await?;
let site = ApubSite::from_json(json, &context).await.unwrap(); let site = ApubSite::from_json(json, &context).await?;
let json = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); let json = file_to_json_object("assets/pleroma/objects/person.json")?;
ApubPerson::verify(&json, &url, &context).await.unwrap(); ApubPerson::verify(&json, &url, &context).await?;
let person = ApubPerson::from_json(json, &context).await.unwrap(); let person = ApubPerson::from_json(json, &context).await?;
assert_eq!(person.actor_id, url.into()); assert_eq!(person.actor_id, url.into());
assert_eq!(person.name, "lanodan"); assert_eq!(person.name, "lanodan");
assert!(!person.local); assert!(!person.local);
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
assert_eq!(person.bio.as_ref().unwrap().len(), 873); assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(873));
cleanup((person, site), &context).await; cleanup((person, site), &context).await?;
Ok(())
} }
async fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) { async fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) -> LemmyResult<()> {
DbPerson::delete(&mut context.pool(), data.0.id) DbPerson::delete(&mut context.pool(), data.0.id).await?;
.await Site::delete(&mut context.pool(), data.1.id).await?;
.unwrap(); Ok(())
Site::delete(&mut context.pool(), data.1.id).await.unwrap();
} }
} }

View file

@ -292,9 +292,6 @@ impl Object for ApubPost {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{ use crate::{
objects::{ objects::{
@ -307,44 +304,47 @@ mod tests {
protocol::tests::file_to_json_object, protocol::tests::file_to_json_object,
}; };
use lemmy_db_schema::source::site::Site; use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_post() { async fn test_parse_lemmy_post() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let (person, site) = parse_lemmy_person(&context).await; let (person, site) = parse_lemmy_person(&context).await?;
let community = parse_lemmy_community(&context).await; let community = parse_lemmy_community(&context).await?;
let json = file_to_json_object("assets/lemmy/objects/page.json").unwrap(); let json = file_to_json_object("assets/lemmy/objects/page.json")?;
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/post/55143")?;
ApubPost::verify(&json, &url, &context).await.unwrap(); ApubPost::verify(&json, &url, &context).await?;
let post = ApubPost::from_json(json, &context).await.unwrap(); let post = ApubPost::from_json(json, &context).await?;
assert_eq!(post.ap_id, url.into()); assert_eq!(post.ap_id, url.into());
assert_eq!(post.name, "Post title"); assert_eq!(post.name, "Post title");
assert!(post.body.is_some()); assert!(post.body.is_some());
assert_eq!(post.body.as_ref().unwrap().len(), 45); assert_eq!(post.body.as_ref().map(std::string::String::len), Some(45));
assert!(!post.locked); assert!(!post.locked);
assert!(!post.featured_community); assert!(!post.featured_community);
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
cleanup(&context, person, site, community, post).await; cleanup(&context, person, site, community, post).await?;
Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_convert_mastodon_post_title() { async fn test_convert_mastodon_post_title() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let (person, site) = parse_lemmy_person(&context).await; let (person, site) = parse_lemmy_person(&context).await?;
let community = parse_lemmy_community(&context).await; let community = parse_lemmy_community(&context).await?;
let json = file_to_json_object("assets/mastodon/objects/page.json").unwrap(); let json = file_to_json_object("assets/mastodon/objects/page.json")?;
let post = ApubPost::from_json(json, &context).await.unwrap(); let post = ApubPost::from_json(json, &context).await?;
assert_eq!(post.name, "Variable never resetting at refresh"); assert_eq!(post.name, "Variable never resetting at refresh");
cleanup(&context, person, site, community, post).await; cleanup(&context, person, site, community, post).await?;
Ok(())
} }
async fn cleanup( async fn cleanup(
@ -353,14 +353,11 @@ mod tests {
site: ApubSite, site: ApubSite,
community: ApubCommunity, community: ApubCommunity,
post: ApubPost, post: ApubPost,
) { ) -> LemmyResult<()> {
Post::delete(&mut context.pool(), post.id).await.unwrap(); Post::delete(&mut context.pool(), post.id).await?;
Person::delete(&mut context.pool(), person.id) Person::delete(&mut context.pool(), person.id).await?;
.await Community::delete(&mut context.pool(), community.id).await?;
.unwrap(); Site::delete(&mut context.pool(), site.id).await?;
Community::delete(&mut context.pool(), community.id) Ok(())
.await
.unwrap();
Site::delete(&mut context.pool(), site.id).await.unwrap();
} }
} }

View file

@ -141,9 +141,6 @@ impl Object for ApubPrivateMessage {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*; use super::*;
use crate::{ use crate::{
objects::{ objects::{
@ -155,90 +152,75 @@ mod tests {
}; };
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use lemmy_db_schema::source::site::Site; use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use serial_test::serial; use serial_test::serial;
async fn prepare_comment_test( async fn prepare_comment_test(
url: &Url, url: &Url,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> (ApubPerson, ApubPerson, ApubSite) { ) -> LemmyResult<(ApubPerson, ApubPerson, ApubSite)> {
let context2 = context.reset_request_count(); let context2 = context.reset_request_count();
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json").unwrap(); let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json")?;
let site = parse_lemmy_instance(&context2).await; let site = parse_lemmy_instance(&context2).await?;
ApubPerson::verify(&lemmy_person, url, &context2) ApubPerson::verify(&lemmy_person, url, &context2).await?;
.await let person1 = ApubPerson::from_json(lemmy_person, &context2).await?;
.unwrap(); let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json")?;
let person1 = ApubPerson::from_json(lemmy_person, &context2) let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan")?;
.await ApubPerson::verify(&pleroma_person, &pleroma_url, &context2).await?;
.unwrap(); let person2 = ApubPerson::from_json(pleroma_person, &context2).await?;
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); Ok((person1, person2, site))
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
ApubPerson::verify(&pleroma_person, &pleroma_url, &context2)
.await
.unwrap();
let person2 = ApubPerson::from_json(pleroma_person, &context2)
.await
.unwrap();
(person1, person2, site)
} }
async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &Data<LemmyContext>) { async fn cleanup(
Person::delete(&mut context.pool(), data.0.id) data: (ApubPerson, ApubPerson, ApubSite),
.await context: &Data<LemmyContext>,
.unwrap(); ) -> LemmyResult<()> {
Person::delete(&mut context.pool(), data.1.id) Person::delete(&mut context.pool(), data.0.id).await?;
.await Person::delete(&mut context.pool(), data.1.id).await?;
.unwrap(); Site::delete(&mut context.pool(), data.2.id).await?;
Site::delete(&mut context.pool(), data.2.id).await.unwrap(); Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_lemmy_pm() { async fn test_parse_lemmy_pm() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
let data = prepare_comment_test(&url, &context).await; let data = prepare_comment_test(&url, &context).await?;
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json").unwrap(); let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json")?;
ApubPrivateMessage::verify(&json, &url, &context) ApubPrivateMessage::verify(&json, &url, &context).await?;
.await let pm = ApubPrivateMessage::from_json(json.clone(), &context).await?;
.unwrap();
let pm = ApubPrivateMessage::from_json(json.clone(), &context)
.await
.unwrap();
assert_eq!(pm.ap_id.clone(), url.into()); assert_eq!(pm.ap_id.clone(), url.into());
assert_eq!(pm.content.len(), 20); assert_eq!(pm.content.len(), 20);
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
let pm_id = pm.id; let pm_id = pm.id;
let to_apub = pm.into_json(&context).await.unwrap(); let to_apub = pm.into_json(&context).await?;
assert_json_include!(actual: json, expected: to_apub); assert_json_include!(actual: json, expected: to_apub);
PrivateMessage::delete(&mut context.pool(), pm_id) PrivateMessage::delete(&mut context.pool(), pm_id).await?;
.await cleanup(data, &context).await?;
.unwrap(); Ok(())
cleanup(data, &context).await;
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_parse_pleroma_pm() { async fn test_parse_pleroma_pm() -> LemmyResult<()> {
let context = init_context().await; let context = init_context().await?;
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
let data = prepare_comment_test(&url, &context).await; let data = prepare_comment_test(&url, &context).await?;
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap(); let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2")?;
let json = file_to_json_object("assets/pleroma/objects/chat_message.json").unwrap(); let json = file_to_json_object("assets/pleroma/objects/chat_message.json")?;
ApubPrivateMessage::verify(&json, &pleroma_url, &context) ApubPrivateMessage::verify(&json, &pleroma_url, &context).await?;
.await let pm = ApubPrivateMessage::from_json(json, &context).await?;
.unwrap();
let pm = ApubPrivateMessage::from_json(json, &context).await.unwrap();
assert_eq!(pm.ap_id, pleroma_url.into()); assert_eq!(pm.ap_id, pleroma_url.into());
assert_eq!(pm.content.len(), 3); assert_eq!(pm.content.len(), 3);
assert_eq!(context.request_count(), 0); assert_eq!(context.request_count(), 0);
PrivateMessage::delete(&mut context.pool(), pm.id) PrivateMessage::delete(&mut context.pool(), pm.id).await?;
.await cleanup(data, &context).await?;
.unwrap(); Ok(())
cleanup(data, &context).await;
} }
} }

View file

@ -3,18 +3,16 @@ pub mod undo_block_user;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_block() { fn test_parse_lemmy_block() -> LemmyResult<()> {
test_parse_lemmy_item::<BlockUser>("assets/lemmy/activities/block/block_user.json").unwrap(); test_parse_lemmy_item::<BlockUser>("assets/lemmy/activities/block/block_user.json")?;
test_parse_lemmy_item::<UndoBlockUser>("assets/lemmy/activities/block/undo_block_user.json") test_parse_lemmy_item::<UndoBlockUser>("assets/lemmy/activities/block/undo_block_user.json")?;
.unwrap(); Ok(())
} }
} }

View file

@ -7,9 +7,6 @@ pub mod update;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::community::{ activities::community::{
announce::AnnounceActivity, announce::AnnounceActivity,
@ -21,37 +18,32 @@ mod tests {
}, },
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_community_activities() { fn test_parse_lemmy_community_activities() -> LemmyResult<()> {
test_parse_lemmy_item::<AnnounceActivity>( test_parse_lemmy_item::<AnnounceActivity>(
"assets/lemmy/activities/community/announce_create_page.json", "assets/lemmy/activities/community/announce_create_page.json",
) )?;
.unwrap();
test_parse_lemmy_item::<CollectionAdd>("assets/lemmy/activities/community/add_mod.json") test_parse_lemmy_item::<CollectionAdd>("assets/lemmy/activities/community/add_mod.json")?;
.unwrap(); test_parse_lemmy_item::<CollectionRemove>("assets/lemmy/activities/community/remove_mod.json")?;
test_parse_lemmy_item::<CollectionRemove>("assets/lemmy/activities/community/remove_mod.json")
.unwrap();
test_parse_lemmy_item::<CollectionAdd>( test_parse_lemmy_item::<CollectionAdd>(
"assets/lemmy/activities/community/add_featured_post.json", "assets/lemmy/activities/community/add_featured_post.json",
) )?;
.unwrap();
test_parse_lemmy_item::<CollectionRemove>( test_parse_lemmy_item::<CollectionRemove>(
"assets/lemmy/activities/community/remove_featured_post.json", "assets/lemmy/activities/community/remove_featured_post.json",
) )?;
.unwrap();
test_parse_lemmy_item::<LockPage>("assets/lemmy/activities/community/lock_page.json").unwrap(); test_parse_lemmy_item::<LockPage>("assets/lemmy/activities/community/lock_page.json")?;
test_parse_lemmy_item::<UndoLockPage>("assets/lemmy/activities/community/undo_lock_page.json") test_parse_lemmy_item::<UndoLockPage>("assets/lemmy/activities/community/undo_lock_page.json")?;
.unwrap();
test_parse_lemmy_item::<UpdateCommunity>( test_parse_lemmy_item::<UpdateCommunity>(
"assets/lemmy/activities/community/update_community.json", "assets/lemmy/activities/community/update_community.json",
) )?;
.unwrap();
test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json").unwrap(); test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json")?;
Ok(())
} }
} }

View file

@ -4,9 +4,6 @@ pub mod page;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::create_or_update::{ activities::create_or_update::{
chat_message::CreateOrUpdateChatMessage, chat_message::CreateOrUpdateChatMessage,
@ -15,24 +12,22 @@ mod tests {
}, },
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_create_or_update() { fn test_parse_lemmy_create_or_update() -> LemmyResult<()> {
test_parse_lemmy_item::<CreateOrUpdatePage>( test_parse_lemmy_item::<CreateOrUpdatePage>(
"assets/lemmy/activities/create_or_update/create_page.json", "assets/lemmy/activities/create_or_update/create_page.json",
) )?;
.unwrap();
test_parse_lemmy_item::<CreateOrUpdatePage>( test_parse_lemmy_item::<CreateOrUpdatePage>(
"assets/lemmy/activities/create_or_update/update_page.json", "assets/lemmy/activities/create_or_update/update_page.json",
) )?;
.unwrap();
test_parse_lemmy_item::<CreateOrUpdateNote>( test_parse_lemmy_item::<CreateOrUpdateNote>(
"assets/lemmy/activities/create_or_update/create_note.json", "assets/lemmy/activities/create_or_update/create_note.json",
) )?;
.unwrap();
test_parse_lemmy_item::<CreateOrUpdateChatMessage>( test_parse_lemmy_item::<CreateOrUpdateChatMessage>(
"assets/lemmy/activities/create_or_update/create_private_message.json", "assets/lemmy/activities/create_or_update/create_private_message.json",
) )?;
.unwrap(); Ok(())
} }
} }

View file

@ -40,6 +40,9 @@ pub struct Delete {
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
/// deleting their own content. /// deleting their own content.
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
/// Nonstandard field, only valid if object refers to a Person. If present, all content from the
/// user should be deleted along with the account
pub(crate) remove_data: Option<bool>,
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -52,6 +55,7 @@ impl InCommunity for Delete {
post.community_id post.community_id
} }
DeletableObjects::Post(p) => p.community_id, DeletableObjects::Post(p) => p.community_id,
DeletableObjects::Person(_) => return Err(anyhow!("Person is not part of community").into()),
DeletableObjects::PrivateMessage(_) => { DeletableObjects::PrivateMessage(_) => {
return Err(anyhow!("Private message is not part of community").into()) return Err(anyhow!("Private message is not part of community").into())
} }

View file

@ -4,31 +4,27 @@ pub mod undo_delete;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete}, activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_deletion() { fn test_parse_lemmy_deletion() -> LemmyResult<()> {
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json").unwrap(); test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json")?;
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json").unwrap(); test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json")?;
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_remove_note.json") test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_remove_note.json")?;
.unwrap(); test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json")?;
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json") test_parse_lemmy_item::<Delete>(
.unwrap(); "assets/lemmy/activities/deletion/delete_private_message.json",
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_private_message.json") )?;
.unwrap();
test_parse_lemmy_item::<UndoDelete>( test_parse_lemmy_item::<UndoDelete>(
"assets/lemmy/activities/deletion/undo_delete_private_message.json", "assets/lemmy/activities/deletion/undo_delete_private_message.json",
) )?;
.unwrap();
test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json") test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json")?;
.unwrap(); Ok(())
} }
} }

View file

@ -4,19 +4,17 @@ pub mod undo_follow;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow}, activities::following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_accept_follow() { fn test_parse_lemmy_accept_follow() -> LemmyResult<()> {
test_parse_lemmy_item::<Follow>("assets/lemmy/activities/following/follow.json").unwrap(); test_parse_lemmy_item::<Follow>("assets/lemmy/activities/following/follow.json")?;
test_parse_lemmy_item::<AcceptFollow>("assets/lemmy/activities/following/accept.json").unwrap(); test_parse_lemmy_item::<AcceptFollow>("assets/lemmy/activities/following/accept.json")?;
test_parse_lemmy_item::<UndoFollow>("assets/lemmy/activities/following/undo_follow.json") test_parse_lemmy_item::<UndoFollow>("assets/lemmy/activities/following/undo_follow.json")?;
.unwrap(); Ok(())
} }
} }

View file

@ -16,9 +16,6 @@ pub enum CreateOrUpdateType {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::{ activities::{
community::announce::AnnounceActivity, community::announce::AnnounceActivity,
@ -29,58 +26,66 @@ mod tests {
}, },
tests::test_json, tests::test_json,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_smithereen_activities() { fn test_parse_smithereen_activities() -> LemmyResult<()> {
test_json::<CreateOrUpdateNote>("assets/smithereen/activities/create_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/smithereen/activities/create_note.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_pleroma_activities() { fn test_parse_pleroma_activities() -> LemmyResult<()> {
test_json::<CreateOrUpdateNote>("assets/pleroma/activities/create_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/pleroma/activities/create_note.json")?;
test_json::<Delete>("assets/pleroma/activities/delete.json").unwrap(); test_json::<Delete>("assets/pleroma/activities/delete.json")?;
test_json::<Follow>("assets/pleroma/activities/follow.json").unwrap(); test_json::<Follow>("assets/pleroma/activities/follow.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_mastodon_activities() { fn test_parse_mastodon_activities() -> LemmyResult<()> {
test_json::<CreateOrUpdateNote>("assets/mastodon/activities/create_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/mastodon/activities/create_note.json")?;
test_json::<Delete>("assets/mastodon/activities/delete.json").unwrap(); test_json::<Delete>("assets/mastodon/activities/delete.json")?;
test_json::<Follow>("assets/mastodon/activities/follow.json").unwrap(); test_json::<Follow>("assets/mastodon/activities/follow.json")?;
test_json::<UndoFollow>("assets/mastodon/activities/undo_follow.json").unwrap(); test_json::<UndoFollow>("assets/mastodon/activities/undo_follow.json")?;
test_json::<Vote>("assets/mastodon/activities/like_page.json").unwrap(); test_json::<Vote>("assets/mastodon/activities/like_page.json")?;
test_json::<UndoVote>("assets/mastodon/activities/undo_like_page.json").unwrap(); test_json::<UndoVote>("assets/mastodon/activities/undo_like_page.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_lotide_activities() { fn test_parse_lotide_activities() -> LemmyResult<()> {
test_json::<Follow>("assets/lotide/activities/follow.json").unwrap(); test_json::<Follow>("assets/lotide/activities/follow.json")?;
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page.json").unwrap(); test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page.json")?;
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page_image.json").unwrap(); test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page_image.json")?;
test_json::<CreateOrUpdateNote>("assets/lotide/activities/create_note_reply.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/lotide/activities/create_note_reply.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_friendica_activities() { fn test_parse_friendica_activities() -> LemmyResult<()> {
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_1.json").unwrap(); test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_1.json")?;
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_2.json").unwrap(); test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_2.json")?;
test_json::<CreateOrUpdateNote>("assets/friendica/activities/create_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/friendica/activities/create_note.json")?;
test_json::<CreateOrUpdateNote>("assets/friendica/activities/update_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/friendica/activities/update_note.json")?;
test_json::<Delete>("assets/friendica/activities/delete.json").unwrap(); test_json::<Delete>("assets/friendica/activities/delete.json")?;
test_json::<Vote>("assets/friendica/activities/like_page.json").unwrap(); test_json::<Vote>("assets/friendica/activities/like_page.json")?;
test_json::<Vote>("assets/friendica/activities/dislike_page.json").unwrap(); test_json::<Vote>("assets/friendica/activities/dislike_page.json")?;
test_json::<UndoVote>("assets/friendica/activities/undo_dislike_page.json").unwrap(); test_json::<UndoVote>("assets/friendica/activities/undo_dislike_page.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_gnusocial_activities() { fn test_parse_gnusocial_activities() -> LemmyResult<()> {
test_json::<CreateOrUpdatePage>("assets/gnusocial/activities/create_page.json").unwrap(); test_json::<CreateOrUpdatePage>("assets/gnusocial/activities/create_page.json")?;
test_json::<CreateOrUpdateNote>("assets/gnusocial/activities/create_note.json").unwrap(); test_json::<CreateOrUpdateNote>("assets/gnusocial/activities/create_note.json")?;
test_json::<Vote>("assets/gnusocial/activities/like_note.json").unwrap(); test_json::<Vote>("assets/gnusocial/activities/like_note.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_peertube_activities() { fn test_parse_peertube_activities() -> LemmyResult<()> {
test_json::<AnnounceActivity>("assets/peertube/activities/announce_video.json").unwrap(); test_json::<AnnounceActivity>("assets/peertube/activities/announce_video.json")?;
Ok(())
} }
} }

View file

@ -3,22 +3,19 @@ pub mod vote;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
activities::voting::{undo_vote::UndoVote, vote::Vote}, activities::voting::{undo_vote::UndoVote, vote::Vote},
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_voting() { fn test_parse_lemmy_voting() -> LemmyResult<()> {
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/like_note.json").unwrap(); test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/like_note.json")?;
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/dislike_page.json").unwrap(); test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/dislike_page.json")?;
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_like_note.json") test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_like_note.json")?;
.unwrap(); test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_dislike_page.json")?;
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_dislike_page.json") Ok(())
.unwrap();
} }
} }

View file

@ -6,9 +6,6 @@ pub(crate) mod group_outbox;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
collections::{ collections::{
empty_outbox::EmptyOutbox, empty_outbox::EmptyOutbox,
@ -19,23 +16,23 @@ mod tests {
}, },
tests::{test_json, test_parse_lemmy_item}, tests::{test_json, test_parse_lemmy_item},
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_lemmy_collections() { fn test_parse_lemmy_collections() -> LemmyResult<()> {
test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json") test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json")?;
.unwrap();
let outbox = let outbox =
test_parse_lemmy_item::<GroupOutbox>("assets/lemmy/collections/group_outbox.json").unwrap(); test_parse_lemmy_item::<GroupOutbox>("assets/lemmy/collections/group_outbox.json")?;
assert_eq!(outbox.ordered_items.len() as i32, outbox.total_items); assert_eq!(outbox.ordered_items.len() as i32, outbox.total_items);
test_parse_lemmy_item::<GroupFeatured>("assets/lemmy/collections/group_featured_posts.json") test_parse_lemmy_item::<GroupFeatured>("assets/lemmy/collections/group_featured_posts.json")?;
.unwrap(); test_parse_lemmy_item::<GroupModerators>("assets/lemmy/collections/group_moderators.json")?;
test_parse_lemmy_item::<GroupModerators>("assets/lemmy/collections/group_moderators.json") test_parse_lemmy_item::<EmptyOutbox>("assets/lemmy/collections/person_outbox.json")?;
.unwrap(); Ok(())
test_parse_lemmy_item::<EmptyOutbox>("assets/lemmy/collections/person_outbox.json").unwrap();
} }
#[test] #[test]
fn test_parse_mastodon_collections() { fn test_parse_mastodon_collections() -> LemmyResult<()> {
test_json::<GroupFeatured>("assets/mastodon/collections/featured.json").unwrap(); test_json::<GroupFeatured>("assets/mastodon/collections/featured.json")?;
Ok(())
} }
} }

View file

@ -89,9 +89,6 @@ pub trait InCommunity {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use activitypub_federation::protocol::context::WithContext; use activitypub_federation::protocol::context::WithContext;
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;

View file

@ -95,9 +95,6 @@ impl LanguageTag {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{ use crate::protocol::{
objects::{ objects::{
chat_message::ChatMessage, chat_message::ChatMessage,
@ -110,77 +107,87 @@ mod tests {
}, },
tests::{test_json, test_parse_lemmy_item}, tests::{test_json, test_parse_lemmy_item},
}; };
use lemmy_utils::error::LemmyResult;
#[test] #[test]
fn test_parse_objects_lemmy() { fn test_parse_objects_lemmy() -> LemmyResult<()> {
test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json").unwrap(); test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json")?;
test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json").unwrap(); test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json")?;
test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json").unwrap(); test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json")?;
test_parse_lemmy_item::<Page>("assets/lemmy/objects/page.json").unwrap(); test_parse_lemmy_item::<Page>("assets/lemmy/objects/page.json")?;
test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json").unwrap(); test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json")?;
test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json").unwrap(); test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json")?;
test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json").unwrap(); test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_objects_pleroma() { fn test_parse_objects_pleroma() -> LemmyResult<()> {
test_json::<Person>("assets/pleroma/objects/person.json").unwrap(); test_json::<Person>("assets/pleroma/objects/person.json")?;
test_json::<Note>("assets/pleroma/objects/note.json").unwrap(); test_json::<Note>("assets/pleroma/objects/note.json")?;
test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json").unwrap(); test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_objects_smithereen() { fn test_parse_objects_smithereen() -> LemmyResult<()> {
test_json::<Person>("assets/smithereen/objects/person.json").unwrap(); test_json::<Person>("assets/smithereen/objects/person.json")?;
test_json::<Note>("assets/smithereen/objects/note.json").unwrap(); test_json::<Note>("assets/smithereen/objects/note.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_objects_mastodon() { fn test_parse_objects_mastodon() -> LemmyResult<()> {
test_json::<Person>("assets/mastodon/objects/person.json").unwrap(); test_json::<Person>("assets/mastodon/objects/person.json")?;
test_json::<Note>("assets/mastodon/objects/note.json").unwrap(); test_json::<Note>("assets/mastodon/objects/note.json")?;
test_json::<Page>("assets/mastodon/objects/page.json").unwrap(); test_json::<Page>("assets/mastodon/objects/page.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_objects_lotide() { fn test_parse_objects_lotide() -> LemmyResult<()> {
test_json::<Group>("assets/lotide/objects/group.json").unwrap(); test_json::<Group>("assets/lotide/objects/group.json")?;
test_json::<Person>("assets/lotide/objects/person.json").unwrap(); test_json::<Person>("assets/lotide/objects/person.json")?;
test_json::<Note>("assets/lotide/objects/note.json").unwrap(); test_json::<Note>("assets/lotide/objects/note.json")?;
test_json::<Page>("assets/lotide/objects/page.json").unwrap(); test_json::<Page>("assets/lotide/objects/page.json")?;
test_json::<Tombstone>("assets/lotide/objects/tombstone.json").unwrap(); test_json::<Tombstone>("assets/lotide/objects/tombstone.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_object_friendica() { fn test_parse_object_friendica() -> LemmyResult<()> {
test_json::<Person>("assets/friendica/objects/person_1.json").unwrap(); test_json::<Person>("assets/friendica/objects/person_1.json")?;
test_json::<Person>("assets/friendica/objects/person_2.json").unwrap(); test_json::<Person>("assets/friendica/objects/person_2.json")?;
test_json::<Page>("assets/friendica/objects/page_1.json").unwrap(); test_json::<Page>("assets/friendica/objects/page_1.json")?;
test_json::<Page>("assets/friendica/objects/page_2.json").unwrap(); test_json::<Page>("assets/friendica/objects/page_2.json")?;
test_json::<Note>("assets/friendica/objects/note_1.json").unwrap(); test_json::<Note>("assets/friendica/objects/note_1.json")?;
test_json::<Note>("assets/friendica/objects/note_2.json").unwrap(); test_json::<Note>("assets/friendica/objects/note_2.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_object_gnusocial() { fn test_parse_object_gnusocial() -> LemmyResult<()> {
test_json::<Person>("assets/gnusocial/objects/person.json").unwrap(); test_json::<Person>("assets/gnusocial/objects/person.json")?;
test_json::<Group>("assets/gnusocial/objects/group.json").unwrap(); test_json::<Group>("assets/gnusocial/objects/group.json")?;
test_json::<Page>("assets/gnusocial/objects/page.json").unwrap(); test_json::<Page>("assets/gnusocial/objects/page.json")?;
test_json::<Note>("assets/gnusocial/objects/note.json").unwrap(); test_json::<Note>("assets/gnusocial/objects/note.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_object_peertube() { fn test_parse_object_peertube() -> LemmyResult<()> {
test_json::<Person>("assets/peertube/objects/person.json").unwrap(); test_json::<Person>("assets/peertube/objects/person.json")?;
test_json::<Group>("assets/peertube/objects/group.json").unwrap(); test_json::<Group>("assets/peertube/objects/group.json")?;
test_json::<Page>("assets/peertube/objects/video.json").unwrap(); test_json::<Page>("assets/peertube/objects/video.json")?;
test_json::<Note>("assets/peertube/objects/note.json").unwrap(); test_json::<Note>("assets/peertube/objects/note.json")?;
Ok(())
} }
#[test] #[test]
fn test_parse_object_mobilizon() { fn test_parse_object_mobilizon() -> LemmyResult<()> {
test_json::<Group>("assets/mobilizon/objects/group.json").unwrap(); test_json::<Group>("assets/mobilizon/objects/group.json")?;
test_json::<Page>("assets/mobilizon/objects/event.json").unwrap(); test_json::<Page>("assets/mobilizon/objects/event.json")?;
test_json::<Person>("assets/mobilizon/objects/person.json").unwrap(); test_json::<Person>("assets/mobilizon/objects/person.json")?;
Ok(())
} }
} }

View file

@ -242,9 +242,6 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::protocol::{objects::page::Page, tests::test_parse_lemmy_item}; use crate::protocol::{objects::page::Page, tests::test_parse_lemmy_item};
#[test] #[test]

View file

@ -107,7 +107,7 @@ async fn upload(
client_req = client_req.header("X-Forwarded-For", addr.to_string()) client_req = client_req.header("X-Forwarded-For", addr.to_string())
}; };
let res = client_req let res = client_req
.timeout(Duration::from_secs(30)) .timeout(Duration::from_secs(pictrs_config.upload_timeout))
.body(Body::wrap_stream(make_send(body))) .body(Body::wrap_stream(make_send(body)))
.send() .send()
.await .await

View file

@ -89,6 +89,10 @@ pub struct PictrsConfig {
/// disabled. /// disabled.
#[default(true)] #[default(true)]
pub cache_external_link_previews: bool, pub cache_external_link_previews: bool,
/// Timeout for uploading images to pictrs (in seconds)
#[default(30)]
pub upload_timeout: u64,
} }
#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]

@ -1 +1 @@
Subproject commit 8bb90fa4a30e6dc2ba4449794e99d6b3325bebb5 Subproject commit 277e3c335bbb9c00c4a600bd4d655b273393f84a

View file

@ -1,3 +1,5 @@
DROP FUNCTION IF EXISTS hot_rank CASCADE;
SET timezone = 'UTC'; SET timezone = 'UTC';
-- Allow ALTER TABLE ... SET DATA TYPE changing between timestamp and timestamptz to avoid a table rewrite when the session time zone is UTC (Noah Misch) -- Allow ALTER TABLE ... SET DATA TYPE changing between timestamp and timestamptz to avoid a table rewrite when the session time zone is UTC (Noah Misch)

View file

@ -0,0 +1,26 @@
ALTER TABLE person
ADD CONSTRAINT idx_person_inbox_url UNIQUE (inbox_url);
ALTER TABLE community
ADD CONSTRAINT idx_community_inbox_url UNIQUE (inbox_url);
UPDATE
site
SET
inbox_url = inbox_query.inbox
FROM (
SELECT
format('https://%s/site_inbox', DOMAIN) AS inbox
FROM
instance,
site,
local_site
WHERE
instance.id = site.instance_id
AND local_site.id = site.id) AS inbox_query,
instance,
local_site
WHERE
instance.id = site.instance_id
AND local_site.id = site.id;

View file

@ -0,0 +1,30 @@
-- drop unique constraints for inbox columns
ALTER TABLE person
DROP CONSTRAINT idx_person_inbox_url;
ALTER TABLE community
DROP CONSTRAINT idx_community_inbox_url;
-- change site inbox path from /inbox to /site_inbox
-- we dont have any way here to set the correct protocol (http or https) according to tls_enabled, or set
-- the correct port in case of debugging
UPDATE
site
SET
inbox_url = inbox_query.inbox
FROM (
SELECT
format('https://%s/inbox', DOMAIN) AS inbox
FROM
instance,
site,
local_site
WHERE
instance.id = site.instance_id
AND local_site.id = site.id) AS inbox_query,
instance,
local_site
WHERE
instance.id = site.instance_id
AND local_site.id = site.id;

View file

@ -15,7 +15,6 @@ use lemmy_api_common::{
generate_inbox_url, generate_inbox_url,
generate_local_apub_endpoint, generate_local_apub_endpoint,
generate_shared_inbox_url, generate_shared_inbox_url,
generate_site_inbox_url,
EndpointType, EndpointType,
}, },
}; };
@ -50,8 +49,8 @@ pub async fn run_advanced_migrations(
comment_updates_2020_04_03(pool, protocol_and_hostname).await?; comment_updates_2020_04_03(pool, protocol_and_hostname).await?;
private_message_updates_2020_05_05(pool, protocol_and_hostname).await?; private_message_updates_2020_05_05(pool, protocol_and_hostname).await?;
post_thumbnail_url_updates_2020_07_27(pool, protocol_and_hostname).await?; post_thumbnail_url_updates_2020_07_27(pool, protocol_and_hostname).await?;
apub_columns_2021_02_02(pool).await?; apub_columns_2021_02_02(pool, settings).await?;
instance_actor_2022_01_28(pool, protocol_and_hostname).await?; instance_actor_2022_01_28(pool, protocol_and_hostname, settings).await?;
regenerate_public_keys_2022_07_05(pool).await?; regenerate_public_keys_2022_07_05(pool).await?;
initialize_local_site_2022_10_10(pool, settings).await?; initialize_local_site_2022_10_10(pool, settings).await?;
@ -283,7 +282,10 @@ async fn post_thumbnail_url_updates_2020_07_27(
/// We are setting inbox and follower URLs for local and remote actors alike, because for now /// We are setting inbox and follower URLs for local and remote actors alike, because for now
/// all federated instances are also Lemmy and use the same URL scheme. /// all federated instances are also Lemmy and use the same URL scheme.
async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError> { async fn apub_columns_2021_02_02(
pool: &mut DbPool<'_>,
settings: &Settings,
) -> Result<(), LemmyError> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
info!("Running apub_columns_2021_02_02"); info!("Running apub_columns_2021_02_02");
{ {
@ -295,7 +297,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
for p in &persons { for p in &persons {
let inbox_url_ = generate_inbox_url(&p.actor_id)?; let inbox_url_ = generate_inbox_url(&p.actor_id)?;
let shared_inbox_url_ = generate_shared_inbox_url(&p.actor_id)?; let shared_inbox_url_ = generate_shared_inbox_url(settings)?;
diesel::update(person.find(p.id)) diesel::update(person.find(p.id))
.set(( .set((
inbox_url.eq(inbox_url_), inbox_url.eq(inbox_url_),
@ -321,7 +323,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
for c in &communities { for c in &communities {
let followers_url_ = generate_followers_url(&c.actor_id)?; let followers_url_ = generate_followers_url(&c.actor_id)?;
let inbox_url_ = generate_inbox_url(&c.actor_id)?; let inbox_url_ = generate_inbox_url(&c.actor_id)?;
let shared_inbox_url_ = generate_shared_inbox_url(&c.actor_id)?; let shared_inbox_url_ = generate_shared_inbox_url(settings)?;
diesel::update(community.find(c.id)) diesel::update(community.find(c.id))
.set(( .set((
followers_url.eq(followers_url_), followers_url.eq(followers_url_),
@ -343,6 +345,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
async fn instance_actor_2022_01_28( async fn instance_actor_2022_01_28(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
protocol_and_hostname: &str, protocol_and_hostname: &str,
settings: &Settings,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
info!("Running instance_actor_2021_09_29"); info!("Running instance_actor_2021_09_29");
if let Ok(site_view) = SiteView::read_local(pool).await { if let Ok(site_view) = SiteView::read_local(pool).await {
@ -356,7 +359,7 @@ async fn instance_actor_2022_01_28(
let site_form = SiteUpdateForm { let site_form = SiteUpdateForm {
actor_id: Some(actor_id.clone().into()), actor_id: Some(actor_id.clone().into()),
last_refreshed_at: Some(naive_now()), last_refreshed_at: Some(naive_now()),
inbox_url: Some(generate_site_inbox_url(&actor_id.into())?), inbox_url: Some(generate_shared_inbox_url(settings)?),
private_key: Some(Some(key_pair.private_key)), private_key: Some(Some(key_pair.private_key)),
public_key: Some(key_pair.public_key), public_key: Some(key_pair.public_key),
..Default::default() ..Default::default()
@ -462,7 +465,7 @@ async fn initialize_local_site_2022_10_10(
.private_key(Some(person_keypair.private_key)) .private_key(Some(person_keypair.private_key))
.public_key(person_keypair.public_key) .public_key(person_keypair.public_key)
.inbox_url(Some(generate_inbox_url(&person_actor_id)?)) .inbox_url(Some(generate_inbox_url(&person_actor_id)?))
.shared_inbox_url(Some(generate_shared_inbox_url(&person_actor_id)?)) .shared_inbox_url(Some(generate_shared_inbox_url(settings)?))
.build(); .build();
let person_inserted = Person::create(pool, &person_form).await?; let person_inserted = Person::create(pool, &person_form).await?;
@ -490,7 +493,7 @@ async fn initialize_local_site_2022_10_10(
.instance_id(instance.id) .instance_id(instance.id)
.actor_id(Some(site_actor_id.clone().into())) .actor_id(Some(site_actor_id.clone().into()))
.last_refreshed_at(Some(naive_now())) .last_refreshed_at(Some(naive_now()))
.inbox_url(Some(generate_site_inbox_url(&site_actor_id.into())?)) .inbox_url(Some(generate_shared_inbox_url(settings)?))
.private_key(Some(site_key_pair.private_key)) .private_key(Some(site_key_pair.private_key))
.public_key(Some(site_key_pair.public_key)) .public_key(Some(site_key_pair.public_key))
.build(); .build();