From fa861ff314ffcf1cd3f84ef4b287bf7d99baec20 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sat, 8 Jan 2022 23:00:38 +0900 Subject: [PATCH 01/34] Add activitystreams 0.7.0 to plume-common dependencies --- plume-common/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/plume-common/Cargo.toml b/plume-common/Cargo.toml index ecdc2fee..0a1cf12a 100644 --- a/plume-common/Cargo.toml +++ b/plume-common/Cargo.toml @@ -24,6 +24,7 @@ tokio = "0.1.22" regex-syntax = { version = "0.6.17", default-features = false, features = ["unicode-perl"] } tracing = "0.1.30" askama_escape = "0.10.2" +activitystreams = "0.7.0-alpha.14" [dependencies.chrono] features = ["serde"] From 71b21289ab1c705d7f5e1d6a19a65b9781aa9903 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sat, 8 Jan 2022 23:01:03 +0900 Subject: [PATCH 02/34] Install activitystreams 0.7.0 --- Cargo.lock | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1270f350..db82d872 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "activitystreams" +version = "0.7.0-alpha.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bcc3fbb392890a1942b1e5cca76cba93c8ed24b5ff50004cc3289afaab3f92c" +dependencies = [ + "activitystreams-kinds", + "chrono", + "mime 0.3.16", + "serde 1.0.133", + "serde_json", + "thiserror", + "url 2.2.2", +] + [[package]] name = "activitystreams-derive" version = "0.1.1" @@ -27,6 +42,16 @@ dependencies = [ "syn 0.13.11", ] +[[package]] +name = "activitystreams-kinds" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0784e99afd032199d3ed70cefb8eb3a8d1aef15f7f2c4e68d033c4e12bb6079e" +dependencies = [ + "serde 1.0.133", + "url 2.2.2", +] + [[package]] name = "activitystreams-traits" version = "0.1.0" @@ -3136,6 +3161,7 @@ name = "plume-common" version = "0.7.1" dependencies = [ "activitypub", + "activitystreams", "activitystreams-derive", "activitystreams-traits", "array_tool", @@ -5232,6 +5258,7 @@ dependencies = [ "idna 0.2.3", "matches", "percent-encoding 2.1.0", + "serde 1.0.133", ] [[package]] From 80c0426768ddb900530a37eeafd22550663ce8bc Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sat, 8 Jan 2022 23:17:52 +0900 Subject: [PATCH 03/34] Add activitystreams 0.7.0 to plume-models dependencies --- plume-models/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index 960aae8e..40ecde96 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -35,6 +35,7 @@ riker = "0.4.2" once_cell = "1.5.2" lettre = "0.9.6" native-tls = "0.2.8" +activitystreams = "0.7.0-alpha.14" [dependencies.chrono] features = ["serde"] From 92c0368dd87910f2c67c07cdaddb5b9278cf5acb Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sat, 8 Jan 2022 23:18:09 +0900 Subject: [PATCH 04/34] Install activitystreams 0.7.0 --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index db82d872..d1581945 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3216,6 +3216,7 @@ name = "plume-models" version = "0.7.1" dependencies = [ "activitypub", + "activitystreams", "ammonia", "bcrypt", "chrono", From d4a13a13d4f7a78d0f42275cb18cedec9d956808 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 00:40:45 +0900 Subject: [PATCH 05/34] Add assert-json-diff to dev dependencies of plume-common % cargo add assert-json-diff -p plume-models --dev --- plume-models/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index 40ecde96..ce900298 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -55,6 +55,7 @@ path = "../plume-common" path = "../plume-macro" [dev-dependencies] +assert-json-diff = "2.0.1" diesel_migrations = "1.3.0" [features] From bfaa2fafaf53464fa940e0d2307036fa246acc7c Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:14:46 +0900 Subject: [PATCH 06/34] Install assert-json-diff --- Cargo.lock | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d1581945..0d27ef05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,16 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5" +[[package]] +name = "assert-json-diff" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" +dependencies = [ + "serde 1.0.133", + "serde_json", +] + [[package]] name = "async-trait" version = "0.1.52" @@ -3218,6 +3228,7 @@ dependencies = [ "activitypub", "activitystreams", "ammonia", + "assert-json-diff", "bcrypt", "chrono", "diesel", From 5373a674e1e13c83dfae75d13e249783eb03d458 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:15:04 +0900 Subject: [PATCH 07/34] Add test for Like::to_activity --- plume-models/src/likes.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index fa092af0..da64d9d4 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -174,3 +174,36 @@ impl NewLike { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::diesel::Connection; + use crate::{inbox::tests::fill_database, tests::db}; + use assert_json_diff::assert_json_eq; + use serde_json::{json, to_value}; + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _users, _blogs) = fill_database(&conn); + let post = &posts[0]; + let user = &post.get_authors(&conn)?[0]; + let like = Like::insert(&*conn, NewLike::new(post, user))?; + let act = like.to_activity(&conn).unwrap(); + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing", + "object": "https://plu.me/~/BlogName/testing", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Like", + }); + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } +} From 9ab9d29efbbf01626566ad82c21d88fc58ffb8c1 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:32:14 +0900 Subject: [PATCH 08/34] Remove double slashes --- plume-models/src/likes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index da64d9d4..fceee144 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -165,8 +165,7 @@ impl AsObject for Like { impl NewLike { pub fn new(p: &Post, u: &User) -> Self { - // TODO: this URL is not valid - let ap_url = format!("{}/like/{}", u.ap_url, p.ap_url); + let ap_url = format!("{}like/{}", u.ap_url, p.ap_url); NewLike { post_id: p.id, user_id: u.id, From e2077bed592717d94f7a7649002bbac7b80611dc Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:55:06 +0900 Subject: [PATCH 09/34] Add test for Like::build_undo --- plume-models/src/likes.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index fceee144..2ef6dec6 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -205,4 +205,28 @@ mod tests { Ok(()) }); } + + #[test] + fn build_undo() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _users, _blogs) = fill_database(&conn); + let post = &posts[0]; + let user = &post.get_authors(&conn)?[0]; + let like = Like::insert(&*conn, NewLike::new(post, user))?; + let act = like.build_undo(&*conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing#delete", + "object": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing#delete", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Undo", + }); + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } From e5bc84badfa629def386883606204dbfa0d1d489 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:58:15 +0900 Subject: [PATCH 10/34] Add tests for Reshare::to_activity and build_undo --- plume-models/src/reshares.rs | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 58400196..175b2c0b 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -199,3 +199,60 @@ impl NewReshare { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::diesel::Connection; + use crate::{inbox::tests::fill_database, tests::db}; + use assert_json_diff::assert_json_eq; + use serde_json::{json, to_value}; + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _users, _blogs) = fill_database(&conn); + let post = &posts[0]; + let user = &post.get_authors(&conn)?[0]; + let reshare = Reshare::insert(&*conn, NewReshare::new(post, user))?; + let act = reshare.to_activity(&conn).unwrap(); + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing", + "object": "https://plu.me/~/BlogName/testing", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Announce", + }); + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn build_undo() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _users, _blogs) = fill_database(&conn); + let post = &posts[0]; + let user = &post.get_authors(&conn)?[0]; + let reshare = Reshare::insert(&*conn, NewReshare::new(post, user))?; + let act = reshare.build_undo(&*conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing#delete", + "object": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing#delete", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Announce", + }); + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } +} From f529e803ef6455961c4e8f63ddfd453f16c0091e Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 01:58:39 +0900 Subject: [PATCH 11/34] Fix ap_url of Reshare --- plume-models/src/reshares.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 175b2c0b..2162475b 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -191,7 +191,7 @@ impl AsObject for Reshare { impl NewReshare { pub fn new(p: &Post, u: &User) -> Self { - let ap_url = format!("{}/reshare/{}", u.ap_url, p.ap_url); + let ap_url = format!("{}reshare/{}", u.ap_url, p.ap_url); NewReshare { post_id: p.id, user_id: u.id, From ca6cd534d80e95b5eaedb32c2cc0cab9d8db0189 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 04:58:48 +0900 Subject: [PATCH 12/34] Add tests for Post::to_activity(), create_activity() and update_activity() --- plume-models/src/posts.rs | 150 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index c41c6b2d..b7742054 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -944,7 +944,23 @@ mod tests { use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::safe_string::SafeString; use crate::tests::db; + use assert_json_diff::assert_json_eq; + use chrono::{naive::NaiveDateTime, Datelike, Timelike}; use diesel::Connection; + use serde_json::{json, to_value}; + + fn format_datetime(dt: &NaiveDateTime) -> String { + format!( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", + dt.year(), + dt.month(), + dt.day(), + dt.hour(), + dt.minute(), + dt.second(), + dt.timestamp_subsec_micros() + ) + } // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works @@ -1038,4 +1054,138 @@ mod tests { &article.object.object_props.id_string().unwrap() ); } + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _blogs, _users) = fill_database(&conn); + let post = &posts[0]; + let act = post.to_activity(&conn)?; + + let expected = json!({ + "attributedTo": ["https://plu.me/@/admin/", "https://plu.me/~/BlogName/"], + "cc": [], + "content": "Hello", + "id": "https://plu.me/~/BlogName/testing", + "license": "WTFPL", + "name": "Testing", + "published": format_datetime(&post.creation_date), + "source": { + "content": "", + "mediaType": "text/markdown" + }, + "summary": "", + "tag": [], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Article", + "url": "https://plu.me/~/BlogName/testing" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn create_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _blogs, _users) = fill_database(&conn); + let post = &posts[0]; + let act = post.create_activity(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": [], + "id": "https://plu.me/~/BlogName/testing/activity", + "object": { + "attributedTo": ["https://plu.me/@/admin/", "https://plu.me/~/BlogName/"], + "cc": [], + "content": "Hello", + "id": "https://plu.me/~/BlogName/testing", + "license": "WTFPL", + "name": "Testing", + "published": format_datetime(&post.creation_date), + "source": { + "content": "", + "mediaType": "text/markdown" + }, + "summary": "", + "tag": [], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Article", + "url": "https://plu.me/~/BlogName/testing" + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn update_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _blogs, _users) = fill_database(&conn); + let post = &posts[0]; + let act = post.update_activity(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "cc": [], + "id": "https://plu.me/~/BlogName/testing/update-", + "object": { + "attributedTo": ["https://plu.me/@/admin/", "https://plu.me/~/BlogName/"], + "cc": [], + "content": "Hello", + "id": "https://plu.me/~/BlogName/testing", + "license": "WTFPL", + "name": "Testing", + "published": format_datetime(&post.creation_date), + "source": { + "content": "", + "mediaType": "text/markdown" + }, + "summary": "", + "tag": [], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Article", + "url": "https://plu.me/~/BlogName/testing" + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Update" + }); + let actual = to_value(act)?; + + let id = actual["id"].to_string(); + let (id_pre, id_post) = id.rsplit_once("-").unwrap(); + assert_eq!(post.ap_url, "https://plu.me/~/BlogName/testing"); + assert_eq!( + id_pre, + to_value("\"https://plu.me/~/BlogName/testing/update") + .unwrap() + .as_str() + .unwrap() + ); + assert_eq!(id_post.len(), 11); + assert_eq!( + id_post.matches(char::is_numeric).collect::().len(), + 10 + ); + for (key, value) in actual.as_object().unwrap().into_iter() { + if key == "id" { + continue; + } + assert_eq!(value, expected.get(key).unwrap()); + } + + Ok(()) + }); + } } From 0cbc9438d450a10742116c22478c9762a1b3ea50 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 04:59:12 +0900 Subject: [PATCH 13/34] Complete a slash to Post Create activity's ID --- plume-models/src/posts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index b7742054..b6d915bf 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -413,7 +413,7 @@ impl Post { let article = self.to_activity(conn)?; let mut act = Create::default(); act.object_props - .set_id_string(format!("{}activity", self.ap_url))?; + .set_id_string(format!("{}/activity", self.ap_url))?; act.object_props .set_to_link_vec::(article.object.object_props.to_link_vec()?)?; act.object_props From c1f42836d9d3c2e4b84202651f368d9f79826491 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 05:16:56 +0900 Subject: [PATCH 14/34] Fix variable names --- plume-models/src/posts.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index b6d915bf..7f005d0f 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -1059,7 +1059,7 @@ mod tests { fn to_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _blogs, _users) = fill_database(&conn); + let (posts, _users, _blogs) = fill_database(&conn); let post = &posts[0]; let act = post.to_activity(&conn)?; @@ -1092,7 +1092,7 @@ mod tests { fn create_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _blogs, _users) = fill_database(&conn); + let (posts, _users, _blogs) = fill_database(&conn); let post = &posts[0]; let act = post.create_activity(&conn)?; @@ -1132,7 +1132,7 @@ mod tests { fn update_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _blogs, _users) = fill_database(&conn); + let (posts, _users, _blogs) = fill_database(&conn); let post = &posts[0]; let act = post.update_activity(&conn)?; From 4df2ce5744ee0ad7786f032bb1599808ce553eb0 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 08:08:26 +0900 Subject: [PATCH 15/34] Add mention to test suite for Post activities --- plume-models/src/posts.rs | 50 ++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 7f005d0f..f425f6bc 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -942,6 +942,7 @@ impl From for Arc { mod tests { use super::*; use crate::inbox::{inbox, tests::fill_database, InboxResult}; + use crate::mentions::{Mention, NewMention}; use crate::safe_string::SafeString; use crate::tests::db; use assert_json_diff::assert_json_eq; @@ -962,6 +963,22 @@ mod tests { ) } + fn prepare_activity(conn: &DbConn) -> (Post, Mention, Vec, Vec, Vec) { + let (posts, users, blogs) = fill_database(conn); + let post = &posts[0]; + let mentioned = &users[1]; + let mention = Mention::insert( + &conn, + NewMention { + mentioned_id: mentioned.id, + post_id: Some(post.id), + comment_id: None, + }, + ) + .unwrap(); + (post.to_owned(), mention.to_owned(), posts, users, blogs) + } + // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works #[test] @@ -1059,8 +1076,7 @@ mod tests { fn to_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _users, _blogs) = fill_database(&conn); - let post = &posts[0]; + let (post, _mention, _posts, _users, _blogs) = prepare_activity(&conn); let act = post.to_activity(&conn)?; let expected = json!({ @@ -1076,7 +1092,13 @@ mod tests { "mediaType": "text/markdown" }, "summary": "", - "tag": [], + "tag": [ + { + "href": "https://plu.me/@/user/", + "name": "@user", + "type": "Mention" + } + ], "to": ["https://www.w3.org/ns/activitystreams#Public"], "type": "Article", "url": "https://plu.me/~/BlogName/testing" @@ -1092,8 +1114,7 @@ mod tests { fn create_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _users, _blogs) = fill_database(&conn); - let post = &posts[0]; + let (post, _mention, _posts, _users, _blogs) = prepare_activity(&conn); let act = post.create_activity(&conn)?; let expected = json!({ @@ -1113,7 +1134,13 @@ mod tests { "mediaType": "text/markdown" }, "summary": "", - "tag": [], + "tag": [ + { + "href": "https://plu.me/@/user/", + "name": "@user", + "type": "Mention" + } + ], "to": ["https://www.w3.org/ns/activitystreams#Public"], "type": "Article", "url": "https://plu.me/~/BlogName/testing" @@ -1132,8 +1159,7 @@ mod tests { fn update_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let (posts, _users, _blogs) = fill_database(&conn); - let post = &posts[0]; + let (post, _mention, _posts, _users, _blogs) = prepare_activity(&conn); let act = post.update_activity(&conn)?; let expected = json!({ @@ -1153,7 +1179,13 @@ mod tests { "mediaType": "text/markdown" }, "summary": "", - "tag": [], + "tag": [ + { + "href": "https://plu.me/@/user/", + "name": "@user", + "type": "Mention" + } + ], "to": ["https://www.w3.org/ns/activitystreams#Public"], "type": "Article", "url": "https://plu.me/~/BlogName/testing" From 64838ad864bd23e290ca5d2ca7f54742a13168bd Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 08:19:32 +0900 Subject: [PATCH 16/34] Add test for Follow::to_activity() --- plume-models/src/follows.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index 9450b59b..27a50bbb 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -219,8 +219,10 @@ impl IntoId for Follow { #[cfg(test)] mod tests { use super::*; - use crate::{tests::db, users::tests as user_tests}; + use crate::{tests::db, users::tests as user_tests, users::tests::fill_database}; + use assert_json_diff::assert_json_eq; use diesel::Connection; + use serde_json::{json, to_value}; #[test] fn test_id() { @@ -255,4 +257,37 @@ mod tests { Ok(()) }) } + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let users = fill_database(&conn); + let following = &users[1]; + let follower = &users[2]; + let mut follow = Follow::insert( + &conn, + NewFollow { + follower_id: follower.id, + following_id: following.id, + ap_url: "".into(), + }, + )?; + follow.ap_url = format!("https://plu.me/follows/{}", follow.id); + let act = follow.to_activity(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/other/", + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "id": format!("https://plu.me/follows/{}", follow.id), + "object": "https://plu.me/@/user/", + "to": ["https://plu.me/@/user/"], + "type": "Follow" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } From b97c3fdb87fbd404c9f5d1897784864828d8102e Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 08:29:02 +0900 Subject: [PATCH 17/34] Extract Follow::build_accept --- plume-models/src/follows.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index 27a50bbb..c7eeff44 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -99,11 +99,27 @@ impl Follow { )?; res.notify(conn)?; + let accept = res.build_accept(from, target, follow)?; + broadcast( + &*target, + accept, + vec![from.clone()], + CONFIG.proxy().cloned(), + ); + Ok(res) + } + + pub fn build_accept + IntoId, T>( + &self, + from: &B, + target: &A, + follow: FollowAct, + ) -> Result { let mut accept = Accept::default(); let accept_id = ap_url(&format!( "{}/follow/{}/accept", CONFIG.base_url.as_str(), - &res.id + self.id )); accept.object_props.set_id_string(accept_id)?; accept @@ -116,13 +132,8 @@ impl Follow { .accept_props .set_actor_link::(target.clone().into_id())?; accept.accept_props.set_object_object(follow)?; - broadcast( - &*target, - accept, - vec![from.clone()], - CONFIG.proxy().cloned(), - ); - Ok(res) + + Ok(accept) } pub fn build_undo(&self, conn: &Connection) -> Result { From 5ef76873b7b4fb40bb2682a43742b1874e8deb8e Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 09:12:09 +0900 Subject: [PATCH 18/34] Fix tests --- plume-models/src/likes.rs | 9 ++++++++- plume-models/src/reshares.rs | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 2ef6dec6..9c119177 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -220,7 +220,14 @@ mod tests { "actor": "https://plu.me/@/admin/", "cc": ["https://plu.me/@/admin/followers"], "id": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing#delete", - "object": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing#delete", + "object": { + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/like/https://plu.me/~/BlogName/testing", + "object": "https://plu.me/~/BlogName/testing", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Like", + }, "to": ["https://www.w3.org/ns/activitystreams#Public"], "type": "Undo", }); diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 2162475b..90ca0cc8 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -246,9 +246,16 @@ mod test { "actor": "https://plu.me/@/admin/", "cc": ["https://plu.me/@/admin/followers"], "id": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing#delete", - "object": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing#delete", + "object": { + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": "https://plu.me/@/admin/reshare/https://plu.me/~/BlogName/testing", + "object": "https://plu.me/~/BlogName/testing", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Announce" + }, "to": ["https://www.w3.org/ns/activitystreams#Public"], - "type": "Announce", + "type": "Undo", }); assert_json_eq!(to_value(act)?, expected); From 93a2c6d99fcdc33634ae4c2c3111a0619c66bef7 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 09:12:29 +0900 Subject: [PATCH 19/34] Add tests for Follow::build_accept() and build_undo() --- plume-models/src/follows.rs | 85 +++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index c7eeff44..b1e6fdaf 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -117,7 +117,7 @@ impl Follow { ) -> Result { let mut accept = Accept::default(); let accept_id = ap_url(&format!( - "{}/follow/{}/accept", + "{}/follows/{}/accept", CONFIG.base_url.as_str(), self.id )); @@ -235,6 +235,25 @@ mod tests { use diesel::Connection; use serde_json::{json, to_value}; + fn prepare_activity(conn: &DbConn) -> (Follow, User, User, Vec) { + let users = fill_database(conn); + let following = &users[1]; + let follower = &users[2]; + let mut follow = Follow::insert( + conn, + NewFollow { + follower_id: follower.id, + following_id: following.id, + ap_url: "".into(), + }, + ) + .unwrap(); + // following.ap_url = format!("https://plu.me/follows/{}", follow.id); + follow.ap_url = format!("https://plu.me/follows/{}", follow.id); + + (follow, following.to_owned(), follower.to_owned(), users) + } + #[test] fn test_id() { let conn = db(); @@ -273,18 +292,7 @@ mod tests { fn to_activity() { let conn = db(); conn.test_transaction::<_, Error, _>(|| { - let users = fill_database(&conn); - let following = &users[1]; - let follower = &users[2]; - let mut follow = Follow::insert( - &conn, - NewFollow { - follower_id: follower.id, - following_id: following.id, - ap_url: "".into(), - }, - )?; - follow.ap_url = format!("https://plu.me/follows/{}", follow.id); + let (follow, _following, _follower, _users) = prepare_activity(&conn); let act = follow.to_activity(&conn)?; let expected = json!({ @@ -301,4 +309,55 @@ mod tests { Ok(()) }); } + + #[test] + fn build_accept() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (follow, following, follower, _users) = prepare_activity(&conn); + let act = follow.build_accept(&follower, &following, follow.to_activity(&conn)?)?; + + let expected = json!({ + "actor": "https://plu.me/@/user/", + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "id": format!("https://127.0.0.1:7878/follows/{}/accept", follow.id), + "object": { + "actor": "https://plu.me/@/other/", + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "id": format!("https://plu.me/follows/{}", follow.id), + "object": "https://plu.me/@/user/", + "to": ["https://plu.me/@/user/"], + "type": "Follow" + }, + "to": ["https://plu.me/@/other/"], + "type": "Accept" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn build_undo() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (follow, _following, _follower, _users) = prepare_activity(&conn); + let act = follow.build_undo(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/other/", + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "id": format!("https://plu.me/follows/{}/undo", follow.id), + "object": format!("https://plu.me/follows/{}", follow.id), + "to": ["https://plu.me/@/user/"], + "type": "Undo" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } From 9c177f6286ff0496927b38a3c48877428f51944d Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 09:31:03 +0900 Subject: [PATCH 20/34] Change format_datetime implementation according to feature --- plume-models/src/posts.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index f425f6bc..391ede1f 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -950,6 +950,7 @@ mod tests { use diesel::Connection; use serde_json::{json, to_value}; + #[cfg(feature = "postgres")] fn format_datetime(dt: &NaiveDateTime) -> String { format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", @@ -963,6 +964,19 @@ mod tests { ) } + #[cfg(feature = "sqlite")] + fn format_datetime(dt: &NaiveDateTime) -> String { + format!( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", + dt.year(), + dt.month(), + dt.day(), + dt.hour(), + dt.minute(), + dt.second() + ) + } + fn prepare_activity(conn: &DbConn) -> (Post, Mention, Vec, Vec, Vec) { let (posts, users, blogs) = fill_database(conn); let post = &posts[0]; From 1770336c11ecec103c25d7956b42885398037585 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 12:28:22 +0900 Subject: [PATCH 21/34] Make format_datetime() crate public --- plume-models/src/lib.rs | 28 ++++++++++++++++++++++++++++ plume-models/src/posts.rs | 30 +----------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index fd93a601..546d5997 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -334,6 +334,7 @@ impl SmtpNewWithAddr for smtp::SmtpClient { #[macro_use] mod tests { use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, Connection as Conn, CONFIG}; + use chrono::{naive::NaiveDateTime, Datelike, Timelike}; use diesel::r2d2::ConnectionManager; use plume_common::utils::random_hex; use std::env::temp_dir; @@ -366,6 +367,33 @@ mod tests { pool }; } + + #[cfg(feature = "postgres")] + pub(crate) fn format_datetime(dt: &NaiveDateTime) -> String { + format!( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", + dt.year(), + dt.month(), + dt.day(), + dt.hour(), + dt.minute(), + dt.second(), + dt.timestamp_subsec_micros() + ) + } + + #[cfg(feature = "sqlite")] + pub(crate) fn format_datetime(dt: &NaiveDateTime) -> String { + format!( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", + dt.year(), + dt.month(), + dt.day(), + dt.hour(), + dt.minute(), + dt.second() + ) + } } pub mod admin; diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 391ede1f..65c98735 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -944,39 +944,11 @@ mod tests { use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::mentions::{Mention, NewMention}; use crate::safe_string::SafeString; - use crate::tests::db; + use crate::tests::{db, format_datetime}; use assert_json_diff::assert_json_eq; - use chrono::{naive::NaiveDateTime, Datelike, Timelike}; use diesel::Connection; use serde_json::{json, to_value}; - #[cfg(feature = "postgres")] - fn format_datetime(dt: &NaiveDateTime) -> String { - format!( - "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second(), - dt.timestamp_subsec_micros() - ) - } - - #[cfg(feature = "sqlite")] - fn format_datetime(dt: &NaiveDateTime) -> String { - format!( - "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second() - ) - } - fn prepare_activity(conn: &DbConn) -> (Post, Mention, Vec, Vec, Vec) { let (posts, users, blogs) = fill_database(conn); let post = &posts[0]; From 2087a659f9d56c9b3687f42290d043798bce6cb4 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 13:10:38 +0900 Subject: [PATCH 22/34] Add test to validate comment json --- plume-models/src/comments.rs | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 09f26a35..5339c972 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -404,8 +404,10 @@ mod tests { use super::*; use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::safe_string::SafeString; - use crate::tests::db; + use crate::tests::{db, format_datetime}; + use assert_json_diff::assert_json_eq; use diesel::Connection; + use serde_json::{json, to_value}; // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works @@ -418,7 +420,7 @@ mod tests { let original_comm = Comment::insert( conn, NewComment { - content: SafeString::new("My comment"), + content: SafeString::new("My comment, mentioning to @user"), in_response_to_id: None, post_id: posts[0].id, author_id: users[0].id, @@ -430,13 +432,40 @@ mod tests { ) .unwrap(); let act = original_comm.create_activity(&conn).unwrap(); + + assert_json_eq!(to_value(&act).unwrap(), json!({ + "actor": "https://plu.me/@/admin/", + "cc": ["https://plu.me/@/admin/followers"], + "id": format!("https://plu.me/~/BlogName/testing/comment/{}/activity", original_comm.id), + "object": { + "attributedTo": "https://plu.me/@/admin/", + "content": r###"

My comment, mentioning to @user

+"###, + "id": format!("https://plu.me/~/BlogName/testing/comment/{}", original_comm.id), + "inReplyTo": "https://plu.me/~/BlogName/testing", + "published": format_datetime(&original_comm.creation_date), + "summary": "My CW", + "tag": [ + { + "href": "https://plu.me/@/user/", + "name": "@user", + "type": "Mention" + } + ], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Note" + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create", + })); + inbox( &conn, serde_json::to_value(original_comm.build_delete(&conn).unwrap()).unwrap(), ) .unwrap(); - match inbox(&conn, serde_json::to_value(act).unwrap()).unwrap() { + match inbox(&conn, to_value(act).unwrap()).unwrap() { InboxResult::Commented(c) => { // TODO: one is HTML, the other markdown: assert_eq!(c.content, original_comm.content); assert_eq!(c.in_response_to_id, original_comm.in_response_to_id); From e8153d4b42c51781af247b5511941453303baf5d Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 13:10:54 +0900 Subject: [PATCH 23/34] Fix Comment::to_activity() --- plume-models/src/comments.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 5339c972..89cab5b6 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -16,7 +16,7 @@ use activitypub::{ link, object::{Note, Tombstone}, }; -use chrono::{self, NaiveDateTime}; +use chrono::{self, NaiveDateTime, TimeZone, Utc}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; use plume_common::{ activity_pub::{ @@ -59,7 +59,7 @@ impl Comment { insert!(comments, NewComment, |inserted, conn| { if inserted.ap_url.is_none() { inserted.ap_url = Some(format!( - "{}comment/{}", + "{}/comment/{}", inserted.get_post(conn)?.ap_url, inserted.id )); @@ -129,7 +129,7 @@ impl Comment { |id| Ok(Comment::get(conn, id)?.ap_url.unwrap_or_default()) as Result, )?))?; note.object_props - .set_published_string(chrono::Utc::now().to_rfc3339())?; + .set_published_utctime(Utc.from_utc_datetime(&self.creation_date))?; note.object_props.set_attributed_to_link(author.into_id())?; note.object_props.set_to_link_vec(to)?; note.object_props.set_tag_link_vec( From 05f55fc1caf718492b10ed033fbac05d3cb09339 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 13:11:09 +0900 Subject: [PATCH 24/34] Add https scheme to mention URI in contents --- plume-common/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plume-common/src/utils.rs b/plume-common/src/utils.rs index cbb4db72..17912a02 100644 --- a/plume-common/src/utils.rs +++ b/plume-common/src/utils.rs @@ -271,7 +271,7 @@ pub fn md_to_html<'a>( media_processor: Option>, ) -> (String, HashSet, HashSet) { let base_url = if let Some(base_url) = base_url { - format!("//{}/", base_url) + format!("https://{}/", base_url) } else { "/".to_owned() }; From 4842385ca6434f1e4f89561f6c2ade329c716287 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 20:36:38 +0900 Subject: [PATCH 25/34] Add test about reply --- plume-models/src/comments.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 89cab5b6..7066b95a 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -459,6 +459,41 @@ mod tests { "type": "Create", })); + let reply = Comment::insert( + conn, + NewComment { + content: SafeString::new(""), + in_response_to_id: Some(original_comm.id), + post_id: posts[0].id, + author_id: users[1].id, + ap_url: None, + sensitive: false, + spoiler_text: "".into(), + public_visibility: true, + }, + ) + .unwrap(); + let reply_act = reply.create_activity(&conn).unwrap(); + + assert_json_eq!(to_value(&reply_act).unwrap(), json!({ + "actor": "https://plu.me/@/user/", + "cc": ["https://plu.me/@/user/followers"], + "id": format!("https://plu.me/~/BlogName/testing/comment/{}/activity", reply.id), + "object": { + "attributedTo": "https://plu.me/@/user/", + "content": "", + "id": format!("https://plu.me/~/BlogName/testing/comment/{}", reply.id), + "inReplyTo": format!("https://plu.me/~/BlogName/testing/comment/{}", original_comm.id), + "published": format_datetime(&reply.creation_date), + "summary": "", + "tag": [], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Note" + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create" + })); + inbox( &conn, serde_json::to_value(original_comm.build_delete(&conn).unwrap()).unwrap(), From 65372d20180ed58204e6f2e3f45fe85b55d01c1d Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 20:43:38 +0900 Subject: [PATCH 26/34] Extract comments::tests::prepare_activity() --- plume-models/src/comments.rs | 39 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 7066b95a..8a1e0271 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -402,6 +402,7 @@ impl CommentTree { #[cfg(test)] mod tests { use super::*; + use crate::blogs::Blog; use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::safe_string::SafeString; use crate::tests::{db, format_datetime}; @@ -409,28 +410,34 @@ mod tests { use diesel::Connection; use serde_json::{json, to_value}; + fn prepare_activity(conn: &DbConn) -> (Comment, Vec, Vec, Vec) { + let (posts, users, blogs) = fill_database(&conn); + + let comment = Comment::insert( + conn, + NewComment { + content: SafeString::new("My comment, mentioning to @user"), + in_response_to_id: None, + post_id: posts[0].id, + author_id: users[0].id, + ap_url: None, + sensitive: true, + spoiler_text: "My CW".into(), + public_visibility: true, + }, + ) + .unwrap(); + + (comment, posts, users, blogs) + } + // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works #[test] fn self_federation() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&conn); - - let original_comm = Comment::insert( - conn, - NewComment { - content: SafeString::new("My comment, mentioning to @user"), - in_response_to_id: None, - post_id: posts[0].id, - author_id: users[0].id, - ap_url: None, - sensitive: true, - spoiler_text: "My CW".into(), - public_visibility: true, - }, - ) - .unwrap(); + let (original_comm, posts, users, _blogs) = prepare_activity(&conn); let act = original_comm.create_activity(&conn).unwrap(); assert_json_eq!(to_value(&act).unwrap(), json!({ From 6107842303408704969f95d4bf898e413cb60302 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 9 Jan 2022 21:05:47 +0900 Subject: [PATCH 27/34] Add tests for Comment::to_activity() and build_delete() --- plume-models/src/comments.rs | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 8a1e0271..1da82a36 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -522,4 +522,60 @@ mod tests { Ok(()) }) } + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (comment, _posts, _users, _blogs) = prepare_activity(&conn); + let act = comment.to_activity(&conn)?; + + let expected = json!({ + "attributedTo": "https://plu.me/@/admin/", + "content": r###"

My comment, mentioning to @user

+"###, + "id": format!("https://plu.me/~/BlogName/testing/comment/{}", comment.id), + "inReplyTo": "https://plu.me/~/BlogName/testing", + "published": format_datetime(&comment.creation_date), + "summary": "My CW", + "tag": [ + { + "href": "https://plu.me/@/user/", + "name": "@user", + "type": "Mention" + } + ], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Note" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn build_delete() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (comment, _posts, _users, _blogs) = prepare_activity(&conn); + let act = comment.build_delete(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "id": format!("https://plu.me/~/BlogName/testing/comment/{}#delete", comment.id), + "object": { + "id": format!("https://plu.me/~/BlogName/testing/comment/{}", comment.id), + "type": "Tombstone" + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Delete" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } From e1a598a459fcaf69a15d6e592e659947d1c2bafa Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 15:08:11 +0900 Subject: [PATCH 28/34] Attach avater to sample user --- plume-models/src/users.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 8244075e..7133a769 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -1142,6 +1142,7 @@ pub(crate) mod tests { use super::*; use crate::{ instance::{tests as instance_tests, Instance}, + medias::{Media, NewMedia}, tests::db, Connection as Conn, }; @@ -1179,6 +1180,20 @@ pub(crate) mod tests { Some("invalid_other_password".to_owned()), ) .unwrap(); + let _avatar = Media::insert( + conn, + NewMedia { + file_path: "https://plu.me/media/example.png".into(), + alt_text: "Another user".into(), + is_remote: false, + remote_url: None, + sensitive: false, + content_warning: None, + owner_id: other.id, + }, + ) + .unwrap(); + vec![admin, user, other] } From 7d320e57dace571ea8fbf0ae242009d265edd77d Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 22:04:57 +0900 Subject: [PATCH 29/34] Don't make medias::tests::clean() panic when file not found --- plume-models/src/medias.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs index f4761461..1af5f3b6 100644 --- a/plume-models/src/medias.rs +++ b/plume-models/src/medias.rs @@ -400,7 +400,15 @@ pub(crate) mod tests { pub(crate) fn clean(conn: &Conn) { //used to remove files generated by tests for media in Media::list_all_medias(conn).unwrap() { - media.delete(conn).unwrap(); + if let Some(err) = media.delete(conn).err() { + match &err { + Error::Io(e) => match e.kind() { + std::io::ErrorKind::NotFound => (), + _ => panic!("{:?}", err), + }, + _ => panic!("{:?}", err), + } + } } } From f1cdf4552ff891f2046ed9b676cf78127934d0cf Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 22:18:34 +0900 Subject: [PATCH 30/34] Extract User::outbox_collection() from outbox() for testablity --- plume-models/src/users.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 7133a769..9b2bffff 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -429,6 +429,9 @@ impl User { .map_err(Error::from) } pub fn outbox(&self, conn: &Connection) -> Result> { + Ok(ActivityStream::new(self.outbox_collection(conn)?)) + } + pub fn outbox_collection(&self, conn: &Connection) -> Result { let mut coll = OrderedCollection::default(); let first = &format!("{}?page=1", &self.outbox_url); let last = &format!( @@ -440,7 +443,7 @@ impl User { coll.collection_props.set_last_link(Id::new(last))?; coll.collection_props .set_total_items_u64(self.get_activities_count(conn) as u64)?; - Ok(ActivityStream::new(coll)) + Ok(coll) } pub fn outbox_page( &self, From 3b429909f1dab00ee29d7a9d6fb7cf5ebdf4e222 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 22:18:59 +0900 Subject: [PATCH 31/34] Extract User::outbox_collection_page() from outbox_collection() for testablity --- plume-models/src/users.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 9b2bffff..53b35fa0 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -450,6 +450,15 @@ impl User { conn: &Connection, (min, max): (i32, i32), ) -> Result> { + Ok(ActivityStream::new( + self.outbox_collection_page(conn, (min, max))?, + )) + } + pub fn outbox_collection_page( + &self, + conn: &Connection, + (min, max): (i32, i32), + ) -> Result { let acts = self.get_activities_page(conn, (min, max))?; let n_acts = self.get_activities_count(conn); let mut coll = OrderedCollectionPage::default(); @@ -470,7 +479,7 @@ impl User { coll.collection_props.items = serde_json::to_value(acts)?; coll.collection_page_props .set_part_of_link(Id::new(&self.outbox_url))?; - Ok(ActivityStream::new(coll)) + Ok(coll) } fn fetch_outbox_page(&self, url: &str) -> Result<(Vec, Option)> { let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?; From 113722e4ba21d1297c66fd9eb042adc680000532 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 22:19:44 +0900 Subject: [PATCH 32/34] Add more ActivityPub tests for User --- plume-models/src/users.rs | 197 +++++++++++++++++++++++++++++++++++++- 1 file changed, 192 insertions(+), 5 deletions(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 53b35fa0..6d908661 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -1156,9 +1156,11 @@ pub(crate) mod tests { instance::{tests as instance_tests, Instance}, medias::{Media, NewMedia}, tests::db, - Connection as Conn, + Connection as Conn, ITEMS_PER_PAGE, }; - use diesel::Connection; + use assert_json_diff::assert_json_eq; + use diesel::{Connection, SaveChangesDsl}; + use serde_json::to_value; pub(crate) fn fill_database(conn: &Conn) -> Vec { instance_tests::fill_database(conn); @@ -1182,7 +1184,7 @@ pub(crate) mod tests { Some("invalid_user_password".to_owned()), ) .unwrap(); - let other = NewUser::new_local( + let mut other = NewUser::new_local( conn, "other".to_owned(), "Another user".to_owned(), @@ -1192,10 +1194,10 @@ pub(crate) mod tests { Some("invalid_other_password".to_owned()), ) .unwrap(); - let _avatar = Media::insert( + let avatar = Media::insert( conn, NewMedia { - file_path: "https://plu.me/media/example.png".into(), + file_path: "static/media/example.png".into(), alt_text: "Another user".into(), is_remote: false, remote_url: None, @@ -1205,10 +1207,60 @@ pub(crate) mod tests { }, ) .unwrap(); + other.avatar_id = Some(avatar.id); + let other = other.save_changes::(&*conn).unwrap(); vec![admin, user, other] } + fn fill_pages( + conn: &DbConn, + ) -> ( + Vec, + Vec, + Vec, + ) { + use crate::post_authors::NewPostAuthor; + use crate::posts::NewPost; + + let (mut posts, users, blogs) = crate::inbox::tests::fill_database(conn); + let user = &users[0]; + let blog = &blogs[0]; + + for i in 1..(ITEMS_PER_PAGE * 4 + 3) { + let title = format!("Post {}", i); + let content = format!("Content for post {}.", i); + let post = Post::insert( + conn, + NewPost { + blog_id: blog.id, + slug: title.clone(), + title: title.clone(), + content: SafeString::new(&content), + published: true, + license: "CC-0".into(), + creation_date: None, + ap_url: format!("{}/{}", blog.ap_url, title), + subtitle: "".into(), + source: content, + cover_id: None, + }, + ) + .unwrap(); + PostAuthor::insert( + conn, + NewPostAuthor { + post_id: post.id, + author_id: user.id, + }, + ) + .unwrap(); + posts.push(post); + } + + (posts, users, blogs) + } + #[test] fn find_by() { let conn = db(); @@ -1369,4 +1421,139 @@ pub(crate) mod tests { Ok(()) }); } + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let users = fill_database(&conn); + let user = &users[0]; + let act = user.to_activity(&conn)?; + + let expected = json!({ + "endpoints": { + "sharedInbox": "https://plu.me/inbox" + }, + "followers": "https://plu.me/@/admin/followers", + "following": null, + "id": "https://plu.me/@/admin/", + "inbox": "https://plu.me/@/admin/inbox", + "liked": null, + "name": "The admin", + "outbox": "https://plu.me/@/admin/outbox", + "preferredUsername": "admin", + "publicKey": { + "id": "https://plu.me/@/admin/#main-key", + "owner": "https://plu.me/@/admin/", + "publicKeyPem": user.public_key, + }, + "summary": "

Hello there, I’m the admin

\n", + "type": "Person", + "url": "https://plu.me/@/admin/" + }); + + assert_json_eq!(to_value(act)?, expected); + + let other = &users[2]; + let other_act = other.to_activity(&conn)?; + let expected_other = json!({ + "endpoints": { + "sharedInbox": "https://plu.me/inbox" + }, + "followers": "https://plu.me/@/other/followers", + "following": null, + "icon": { + "url": "https://plu.me/static/media/example.png", + "type": "Image", + }, + "id": "https://plu.me/@/other/", + "inbox": "https://plu.me/@/other/inbox", + "liked": null, + "name": "Another user", + "outbox": "https://plu.me/@/other/outbox", + "preferredUsername": "other", + "publicKey": { + "id": "https://plu.me/@/other/#main-key", + "owner": "https://plu.me/@/other/", + "publicKeyPem": other.public_key, + }, + "summary": "

Hello there, I’m someone else

\n", + "type": "Person", + "url": "https://plu.me/@/other/" + }); + + assert_json_eq!(to_value(other_act)?, expected_other); + + Ok(()) + }); + } + + #[test] + fn delete_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let users = fill_database(&conn); + let user = &users[1]; + let act = user.delete_activity(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/user/", + "cc": [], + "id": "https://plu.me/@/user/#delete", + "object": { + "id": "https://plu.me/@/user/", + "type": "Tombstone", + }, + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Delete", + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn outbox_collection() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (_pages, users, _blogs) = fill_pages(&conn); + let user = &users[0]; + let act = user.outbox_collection(&conn)?; + + let expected = json!({ + "first": "https://plu.me/@/admin/outbox?page=1", + "items": null, + "last": "https://plu.me/@/admin/outbox?page=5", + "totalItems": 51, + "type": "OrderedCollection", + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn outbox_collection_page() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let users = fill_database(&conn); + let user = &users[0]; + let act = user.outbox_collection_page(&conn, (33, 36))?; + + let expected = json!({ + "items": [], + "partOf": "https://plu.me/@/admin/outbox", + "prev": "https://plu.me/@/admin/outbox?page=2", + "type": "OrderedCollectionPage", + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } From 34c374de1a4ba56b26fee1eff6f8e3b953d09dcd Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Mon, 10 Jan 2022 22:20:10 +0900 Subject: [PATCH 33/34] Attach icon field to User activity only whene it has avatar --- plume-models/src/users.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 6d908661..1e27fb19 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -775,13 +775,13 @@ impl User { let mut ap_signature = ApSignature::default(); ap_signature.set_public_key_publickey(public_key)?; - let mut avatar = Image::default(); - avatar.object_props.set_url_string( - self.avatar_id - .and_then(|id| Media::get(conn, id).and_then(|m| m.url()).ok()) - .unwrap_or_default(), - )?; - actor.object_props.set_icon_object(avatar)?; + if let Some(avatar_id) = self.avatar_id { + let mut avatar = Image::default(); + avatar + .object_props + .set_url_string(Media::get(conn, avatar_id)?.url()?)?; + actor.object_props.set_icon_object(avatar)?; + } Ok(CustomPerson::new(actor, ap_signature)) } From 54cbdb236ff063fe5284df4ca60e17abc385f3ea Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Tue, 11 Jan 2022 21:58:01 +0900 Subject: [PATCH 34/34] Add tests for Mention activity --- plume-models/src/mentions.rs | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index d83d48d8..16672781 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -145,3 +145,62 @@ impl Mention { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{inbox::tests::fill_database, tests::db, Error}; + use assert_json_diff::assert_json_eq; + use diesel::Connection; + use serde_json::{json, to_value}; + + #[test] + fn build_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (_posts, users, _blogs) = fill_database(&conn); + let user = &users[0]; + let name = &user.username; + let act = Mention::build_activity(&conn, name)?; + + let expected = json!({ + "href": "https://plu.me/@/admin/", + "name": "@admin", + "type": "Mention", + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn to_activity() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, users, _blogs) = fill_database(&conn); + let post = &posts[0]; + let user = &users[0]; + let mention = Mention::insert( + &conn, + NewMention { + mentioned_id: user.id, + post_id: Some(post.id), + comment_id: None, + }, + )?; + let act = mention.to_activity(&conn)?; + + let expected = json!({ + "href": "https://plu.me/@/admin/", + "name": "@admin", + "type": "Mention", + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } +}