Reorganize federation tests (#2092)

* Reorganize apub protocol tests

* Reorder apub protocol struct members to clarify mandatory/optional fields
This commit is contained in:
Nutomic 2022-02-17 22:04:01 +00:00 committed by GitHub
parent e094989a4c
commit 762b85b27e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 244 additions and 211 deletions

View file

@ -1,23 +1,4 @@
{ {
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"stickied": "as:stickied",
"pt": "https://join-lemmy.org#",
"sc": "http://schema.org#",
"matrixUserId": {
"type": "sc:Text",
"id": "as:alsoKnownAs"
},
"sensitive": "as:sensitive",
"comments_enabled": {
"type": "sc:Boolean",
"id": "pt:commentsEnabled"
},
"moderators": "as:moderators"
},
"https://w3id.org/security/v1"
],
"type": "Service", "type": "Service",
"id": "https://enterprise.lemmy.ml/", "id": "https://enterprise.lemmy.ml/",
"name": "Enterprise", "name": "Enterprise",

View file

@ -16,7 +16,7 @@
"type": "Image", "type": "Image",
"url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png" "url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png"
}, },
"matrix_user_id": "@picard:matrix.org", "matrixUserId": "@picard:matrix.org",
"inbox": "https://enterprise.lemmy.ml/u/picard/inbox", "inbox": "https://enterprise.lemmy.ml/u/picard/inbox",
"outbox": "https://enterprise.lemmy.ml/u/picard/outbox", "outbox": "https://enterprise.lemmy.ml/u/picard/outbox",
"endpoints": { "endpoints": {

View file

@ -1,4 +1,11 @@
{ {
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri"
}
],
"id": "https://mastodon.madrid/users/felix/statuses/107224289116410645/activity", "id": "https://mastodon.madrid/users/felix/statuses/107224289116410645/activity",
"type": "Create", "type": "Create",
"actor": "https://mastodon.madrid/users/felix", "actor": "https://mastodon.madrid/users/felix",

View file

@ -0,0 +1,7 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://masto.asonix.dog/1ea87517-63c5-4118-8831-460ee641b2cf",
"type": "Follow",
"actor": "https://masto.asonix.dog/users/asonix",
"object": "https://ds9.lemmy.ml/c/testcom"
}

View file

@ -0,0 +1,13 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://masto.asonix.dog/users/asonix#follows/449/undo",
"type": "Undo",
"actor": "https://masto.asonix.dog/users/asonix",
"object": {
"id": "https://masto.asonix.dog/1ea87517-63c5-4118-8831-460ee641b2cf",
"type": "Follow",
"actor": "https://masto.asonix.dog/users/asonix",
"object": "https://ds9.lemmy.ml/c/testcom"
}
}

View file

@ -28,5 +28,11 @@
} }
}, },
"sensitive": false, "sensitive": false,
"likes": "https://friends.grishka.me/posts/66561/likes" "likes": "https://friends.grishka.me/posts/66561/likes",
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"sensitive": "as:sensitive"
}
]
} }

View file

@ -138,10 +138,13 @@ impl ApubObject for ApubCommunityModerators {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::objects::{ use crate::{
community::tests::parse_lemmy_community, objects::{
person::tests::parse_lemmy_person, community::tests::parse_lemmy_community,
tests::{file_to_json_object, init_context}, person::tests::parse_lemmy_person,
tests::init_context,
},
protocol::tests::file_to_json_object,
}; };
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::{ use lemmy_db_schema::{

View file

@ -125,7 +125,6 @@ impl ApubObject for ApubComment {
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
tag: maa.tags, tag: maa.tags,
unparsed: Default::default(),
}; };
Ok(note) Ok(note)
@ -208,12 +207,15 @@ impl ApubObject for ApubComment {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::objects::{ use crate::{
community::{tests::parse_lemmy_community, ApubCommunity}, objects::{
instance::ApubSite, community::{tests::parse_lemmy_community, ApubCommunity},
person::{tests::parse_lemmy_person, ApubPerson}, instance::ApubSite,
post::ApubPost, person::{tests::parse_lemmy_person, ApubPerson},
tests::{file_to_json_object, init_context}, post::ApubPost,
tests::init_context,
},
protocol::tests::file_to_json_object,
}; };
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;

View file

@ -102,7 +102,6 @@ impl ApubObject for ApubCommunity {
public_key: self.get_public_key()?, public_key: self.get_public_key()?,
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
}; };
Ok(group) Ok(group)
} }
@ -214,9 +213,9 @@ impl ApubCommunity {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::objects::{ use crate::{
instance::tests::parse_lemmy_instance, objects::{instance::tests::parse_lemmy_instance, tests::init_context},
tests::{file_to_json_object, init_context}, protocol::tests::file_to_json_object,
}; };
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::{source::site::Site, traits::Crud}; use lemmy_db_schema::{source::site::Site, traits::Crud};

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
objects::get_summary_from_string_or_source, objects::get_summary_from_string_or_source,
protocol::{objects::instance::Instance, ImageObject, Source, Unparsed}, protocol::{objects::instance::Instance, ImageObject, Source},
}; };
use activitystreams_kinds::actor::ServiceType; use activitystreams_kinds::actor::ServiceType;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -86,7 +86,6 @@ impl ApubObject for ApubSite {
public_key: self.get_public_key()?, public_key: self.get_public_key()?,
published: convert_datetime(self.published), published: convert_datetime(self.published),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
unparsed: Unparsed::default(),
}; };
Ok(instance) Ok(instance)
} }
@ -186,7 +185,7 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object(
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::objects::tests::{file_to_json_object, init_context}; use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::traits::Crud; use lemmy_db_schema::traits::Crud;
use serial_test::serial; use serial_test::serial;

View file

@ -41,8 +41,7 @@ pub(crate) mod tests {
use lemmy_websocket::{chat_server::ChatServer, LemmyContext}; use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
use reqwest::Client; use reqwest::Client;
use reqwest_middleware::ClientBuilder; use reqwest_middleware::ClientBuilder;
use serde::de::DeserializeOwned; use std::sync::Arc;
use std::{fs::File, io::BufReader, sync::Arc};
use tokio::sync::Mutex; use tokio::sync::Mutex;
// 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.
@ -90,10 +89,4 @@ pub(crate) mod tests {
.start(); .start();
LemmyContext::create(pool, chat_server, client, activity_queue, settings, secret) LemmyContext::create(pool, chat_server, client, activity_queue, settings, secret)
} }
pub(crate) fn file_to_json_object<T: DeserializeOwned>(path: &str) -> Result<T, LemmyError> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}
} }

View file

@ -105,7 +105,6 @@ impl ApubObject for ApubPerson {
}), }),
public_key: self.get_public_key()?, public_key: self.get_public_key()?,
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
inbox: self.inbox_url.clone().into(), inbox: self.inbox_url.clone().into(),
}; };
Ok(person) Ok(person)
@ -204,9 +203,9 @@ pub(crate) mod tests {
use crate::{ use crate::{
objects::{ objects::{
instance::{tests::parse_lemmy_instance, ApubSite}, instance::{tests::parse_lemmy_instance, ApubSite},
tests::{file_to_json_object, init_context}, tests::init_context,
}, },
protocol::objects::instance::Instance, protocol::{objects::instance::Instance, tests::file_to_json_object},
}; };
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::{source::site::Site, traits::Crud}; use lemmy_db_schema::{source::site::Site, traits::Crud};

View file

@ -122,7 +122,6 @@ impl ApubObject for ApubPost {
stickied: Some(self.stickied), stickied: Some(self.stickied),
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
}; };
Ok(page) Ok(page)
} }
@ -207,11 +206,14 @@ impl ApubObject for ApubPost {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::objects::{ use crate::{
community::tests::parse_lemmy_community, objects::{
person::tests::parse_lemmy_person, community::tests::parse_lemmy_community,
post::ApubPost, person::tests::parse_lemmy_person,
tests::{file_to_json_object, init_context}, post::ApubPost,
tests::init_context,
},
protocol::tests::file_to_json_object,
}; };
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::source::site::Site; use lemmy_db_schema::source::site::Site;

View file

@ -90,7 +90,6 @@ impl ApubObject for ApubPrivateMessage {
source: Some(Source::new(self.content.clone())), source: Some(Source::new(self.content.clone())),
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
}; };
Ok(note) Ok(note)
} }
@ -159,9 +158,9 @@ impl ApubObject for ApubPrivateMessage {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::objects::{ use crate::{
person::ApubPerson, objects::{person::ApubPerson, tests::init_context},
tests::{file_to_json_object, init_context}, protocol::tests::file_to_json_object,
}; };
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_apub_lib::activity_queue::create_activity_queue;

View file

@ -17,13 +17,14 @@ pub struct BlockUser {
pub(crate) target: ObjectId<SiteOrCommunity>, pub(crate) target: ObjectId<SiteOrCommunity>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: BlockType, pub(crate) kind: BlockType,
pub(crate) id: Url,
/// Quick and dirty solution. /// Quick and dirty solution.
/// TODO: send a separate Delete activity instead /// TODO: send a separate Delete activity instead
pub(crate) remove_data: Option<bool>, pub(crate) remove_data: Option<bool>,
/// block reason, written to mod log /// block reason, written to mod log
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) id: Url, pub(crate) expires: Option<DateTime<FixedOffset>>,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
pub(crate) expires: Option<DateTime<FixedOffset>>,
} }

View file

@ -8,8 +8,8 @@ mod tests {
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_lemmy_block() { fn test_parse_lemmy_block() {
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").unwrap();
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(); .unwrap();

View file

@ -19,6 +19,7 @@ pub struct UndoBlockUser {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UndoType, pub(crate) kind: UndoType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -17,6 +17,7 @@ pub struct AddMod {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: AddType, pub(crate) kind: AddType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -20,6 +20,7 @@ pub struct AnnounceActivity {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: AnnounceType, pub(crate) kind: AnnounceType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -17,8 +17,8 @@ mod tests {
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_lemmy_community() { fn test_parse_lemmy_community_activities() {
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",
) )

View file

@ -17,6 +17,7 @@ pub struct RemoveMod {
pub(crate) kind: RemoveType, pub(crate) kind: RemoveType,
pub(crate) target: Url, pub(crate) target: Url,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -19,6 +19,7 @@ pub struct Report {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: FlagType, pub(crate) kind: FlagType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -22,6 +22,7 @@ pub struct UpdateCommunity {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UpdateType, pub(crate) kind: UpdateType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -21,6 +21,7 @@ pub struct CreateOrUpdateComment {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: CreateOrUpdateType, pub(crate) kind: CreateOrUpdateType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -4,21 +4,17 @@ pub mod private_message;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::protocol::{
context::WithContext, activities::create_or_update::{
objects::tests::file_to_json_object, comment::CreateOrUpdateComment,
protocol::{ post::CreateOrUpdatePost,
activities::create_or_update::{ private_message::CreateOrUpdatePrivateMessage,
comment::CreateOrUpdateComment,
post::CreateOrUpdatePost,
private_message::CreateOrUpdatePrivateMessage,
},
tests::test_parse_lemmy_item,
}, },
tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_create_or_update() { fn test_parse_lemmy_create_or_update() {
test_parse_lemmy_item::<CreateOrUpdatePost>( test_parse_lemmy_item::<CreateOrUpdatePost>(
"assets/lemmy/activities/create_or_update/create_page.json", "assets/lemmy/activities/create_or_update/create_page.json",
) )
@ -35,23 +31,5 @@ mod tests {
"assets/lemmy/activities/create_or_update/create_private_message.json", "assets/lemmy/activities/create_or_update/create_private_message.json",
) )
.unwrap(); .unwrap();
file_to_json_object::<WithContext<CreateOrUpdateComment>>(
"assets/pleroma/activities/create_note.json",
)
.unwrap();
file_to_json_object::<WithContext<CreateOrUpdateComment>>(
"assets/smithereen/activities/create_note.json",
)
.unwrap();
file_to_json_object::<CreateOrUpdateComment>("assets/mastodon/activities/create_note.json")
.unwrap();
file_to_json_object::<CreateOrUpdatePost>("assets/lotide/activities/create_page.json").unwrap();
file_to_json_object::<CreateOrUpdateComment>("assets/lotide/activities/create_note_reply.json")
.unwrap();
file_to_json_object::<CreateOrUpdateComment>("assets/friendica/activities/create_note.json")
.unwrap();
} }
} }

View file

@ -18,6 +18,7 @@ pub struct CreateOrUpdatePost {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: CreateOrUpdateType, pub(crate) kind: CreateOrUpdateType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -16,6 +16,7 @@ pub struct CreateOrUpdatePrivateMessage {
pub(crate) object: ChatMessage, pub(crate) object: ChatMessage,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: CreateOrUpdateType, pub(crate) kind: CreateOrUpdateType,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -13,16 +13,17 @@ pub struct Delete {
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
pub(crate) object: IdOrNestedObject, pub(crate) object: IdOrNestedObject,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
pub(crate) id: Url,
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
#[serde(default)] #[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
/// 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>,
pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -3,17 +3,13 @@ pub mod undo_delete;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::protocol::{
context::WithContext, activities::deletion::{delete::Delete, undo_delete::UndoDelete},
objects::tests::file_to_json_object, tests::test_parse_lemmy_item,
protocol::{
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
tests::test_parse_lemmy_item,
},
}; };
#[actix_rt::test] #[test]
async fn test_parse_deletion() { fn test_parse_lemmy_deletion() {
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").unwrap();
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").unwrap();
@ -27,8 +23,5 @@ mod tests {
"assets/lemmy/activities/deletion/undo_delete_private_message.json", "assets/lemmy/activities/deletion/undo_delete_private_message.json",
) )
.unwrap(); .unwrap();
file_to_json_object::<WithContext<Delete>>("assets/pleroma/activities/delete.json").unwrap();
file_to_json_object::<WithContext<Delete>>("assets/mastodon/activities/delete.json").unwrap();
} }
} }

View file

@ -14,13 +14,14 @@ pub struct UndoDelete {
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
pub(crate) object: Delete, pub(crate) object: Delete,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
pub(crate) id: Url,
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
#[serde(default)] #[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -15,6 +15,7 @@ pub struct AcceptFollowCommunity {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: AcceptType, pub(crate) kind: AcceptType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -15,6 +15,7 @@ pub struct FollowCommunity {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: FollowType, pub(crate) kind: FollowType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -4,21 +4,17 @@ pub mod undo_follow;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::protocol::{
context::WithContext, activities::following::{
objects::tests::file_to_json_object, accept::AcceptFollowCommunity,
protocol::{ follow::FollowCommunity,
activities::following::{ undo_follow::UndoFollowCommunity,
accept::AcceptFollowCommunity,
follow::FollowCommunity,
undo_follow::UndoFollowCommunity,
},
tests::test_parse_lemmy_item,
}, },
tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_lemmy_accept_follow() { fn test_parse_lemmy_accept_follow() {
test_parse_lemmy_item::<FollowCommunity>("assets/lemmy/activities/following/follow.json") test_parse_lemmy_item::<FollowCommunity>("assets/lemmy/activities/following/follow.json")
.unwrap(); .unwrap();
test_parse_lemmy_item::<AcceptFollowCommunity>("assets/lemmy/activities/following/accept.json") test_parse_lemmy_item::<AcceptFollowCommunity>("assets/lemmy/activities/following/accept.json")
@ -27,8 +23,5 @@ mod tests {
"assets/lemmy/activities/following/undo_follow.json", "assets/lemmy/activities/following/undo_follow.json",
) )
.unwrap(); .unwrap();
file_to_json_object::<WithContext<FollowCommunity>>("assets/pleroma/activities/follow.json")
.unwrap();
} }
} }

View file

@ -15,6 +15,7 @@ pub struct UndoFollowCommunity {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UndoType, pub(crate) kind: UndoType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -13,3 +13,46 @@ pub enum CreateOrUpdateType {
Create, Create,
Update, Update,
} }
#[cfg(test)]
mod tests {
use crate::protocol::{
activities::{
create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
deletion::delete::Delete,
following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
},
tests::test_json,
};
#[test]
fn test_parse_smithereen_activities() {
test_json::<CreateOrUpdateComment>("assets/smithereen/activities/create_note.json").unwrap();
}
#[test]
fn test_parse_pleroma_activities() {
test_json::<CreateOrUpdateComment>("assets/pleroma/activities/create_note.json").unwrap();
test_json::<Delete>("assets/pleroma/activities/delete.json").unwrap();
test_json::<FollowCommunity>("assets/pleroma/activities/follow.json").unwrap();
}
#[test]
fn test_parse_mastodon_activities() {
test_json::<CreateOrUpdateComment>("assets/mastodon/activities/create_note.json").unwrap();
test_json::<Delete>("assets/mastodon/activities/delete.json").unwrap();
test_json::<FollowCommunity>("assets/mastodon/activities/follow.json").unwrap();
test_json::<UndoFollowCommunity>("assets/mastodon/activities/undo_follow.json").unwrap();
}
#[test]
fn test_parse_lotide_activities() {
test_json::<CreateOrUpdatePost>("assets/lotide/activities/create_page.json").unwrap();
test_json::<CreateOrUpdateComment>("assets/lotide/activities/create_note_reply.json").unwrap();
}
#[test]
fn test_parse_friendica_activities() {
test_json::<CreateOrUpdateComment>("assets/friendica/activities/create_note.json").unwrap();
}
}

View file

@ -8,8 +8,8 @@ mod tests {
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_lemmy_voting() { fn test_parse_lemmy_voting() {
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").unwrap();
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").unwrap();

View file

@ -19,6 +19,7 @@ pub struct UndoVote {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UndoType, pub(crate) kind: UndoType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -22,6 +22,7 @@ pub struct Vote {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: VoteType, pub(crate) kind: VoteType,
pub(crate) id: Url, pub(crate) id: Url,
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }

View file

@ -15,8 +15,8 @@ mod tests {
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}; };
#[actix_rt::test] #[test]
async fn test_parse_lemmy_collections() { fn test_parse_lemmy_collections() {
test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json") test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json")
.unwrap(); .unwrap();
let outbox = let outbox =

View file

@ -49,11 +49,21 @@ pub struct Unparsed(HashMap<String, serde_json::Value>);
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use crate::objects::tests::file_to_json_object; use crate::context::WithContext;
use assert_json_diff::assert_json_include; use assert_json_diff::assert_json_include;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use std::collections::HashMap; use std::{collections::HashMap, fs::File, io::BufReader};
pub(crate) fn file_to_json_object<T: DeserializeOwned>(path: &str) -> Result<T, LemmyError> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}
pub(crate) fn test_json<T: DeserializeOwned>(path: &str) -> Result<WithContext<T>, LemmyError> {
file_to_json_object::<WithContext<T>>(path)
}
/// Check that json deserialize -> serialize -> deserialize gives identical file as initial one. /// Check that json deserialize -> serialize -> deserialize gives identical file as initial one.
/// Ensures that there are no breaking changes in sent data. /// Ensures that there are no breaking changes in sent data.

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::{person::ApubPerson, private_message::ApubPrivateMessage}, objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::{Source, Unparsed}, protocol::Source,
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml}; use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
@ -17,12 +17,11 @@ pub struct ChatMessage {
#[serde(deserialize_with = "crate::deserialize_one")] #[serde(deserialize_with = "crate::deserialize_one")]
pub(crate) to: [ObjectId<ApubPerson>; 1], pub(crate) to: [ObjectId<ApubPerson>; 1],
pub(crate) content: String, pub(crate) content: String,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
pub(crate) source: Option<Source>, pub(crate) source: Option<Source>,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }
/// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages /// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages

View file

@ -5,7 +5,7 @@ use crate::{
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
objects::{community::ApubCommunity, get_summary_from_string_or_source}, objects::{community::ApubCommunity, get_summary_from_string_or_source},
protocol::{objects::Endpoints, ImageObject, Source, Unparsed}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use activitystreams_kinds::actor::GroupType; use activitystreams_kinds::actor::GroupType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -27,10 +27,14 @@ pub struct Group {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: GroupType, pub(crate) kind: GroupType,
pub(crate) id: ObjectId<ApubCommunity>, pub(crate) id: ObjectId<ApubCommunity>,
/// username, set at account creation and can never be changed /// username, set at account creation and usually fixed after that
pub(crate) preferred_username: String, pub(crate) preferred_username: String,
/// title (can be changed at any time) /// displayname
pub(crate) name: String, pub(crate) name: String,
pub(crate) inbox: Url,
pub(crate) followers: Url,
pub(crate) public_key: PublicKey,
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) source: Option<Source>, pub(crate) source: Option<Source>,
pub(crate) icon: Option<ImageObject>, pub(crate) icon: Option<ImageObject>,
@ -40,15 +44,10 @@ pub struct Group {
pub(crate) sensitive: Option<bool>, pub(crate) sensitive: Option<bool>,
// lemmy extension // lemmy extension
pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>, pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>,
pub(crate) inbox: Url,
pub(crate) outbox: ObjectId<ApubCommunityOutbox>, pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
pub(crate) followers: Url,
pub(crate) endpoints: Option<Endpoints>, pub(crate) endpoints: Option<Endpoints>,
pub(crate) public_key: PublicKey,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }
impl Group { impl Group {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::instance::ApubSite, objects::instance::ApubSite,
protocol::{ImageObject, Source, Unparsed}, protocol::{ImageObject, Source},
}; };
use activitystreams_kinds::actor::ServiceType; use activitystreams_kinds::actor::ServiceType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -18,6 +18,11 @@ pub struct Instance {
pub(crate) id: ObjectId<ApubSite>, pub(crate) id: ObjectId<ApubSite>,
// site name // site name
pub(crate) name: String, pub(crate) name: String,
pub(crate) inbox: Url,
/// mandatory field in activitypub, lemmy currently serves an empty outbox
pub(crate) outbox: Url,
pub(crate) public_key: PublicKey,
// sidebar // sidebar
pub(crate) content: Option<String>, pub(crate) content: Option<String>,
pub(crate) source: Option<Source>, pub(crate) source: Option<Source>,
@ -28,12 +33,6 @@ pub struct Instance {
pub(crate) icon: Option<ImageObject>, pub(crate) icon: Option<ImageObject>,
/// instance banner /// instance banner
pub(crate) image: Option<ImageObject>, pub(crate) image: Option<ImageObject>,
pub(crate) inbox: Url,
/// mandatory field in activitypub, currently empty in lemmy
pub(crate) outbox: Url,
pub(crate) public_key: PublicKey,
pub(crate) published: DateTime<FixedOffset>, pub(crate) published: DateTime<FixedOffset>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }

View file

@ -17,25 +17,21 @@ pub struct Endpoints {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::protocol::{
context::WithContext, objects::{
objects::tests::file_to_json_object, chat_message::ChatMessage,
protocol::{ group::Group,
objects::{ instance::Instance,
chat_message::ChatMessage, note::Note,
group::Group, page::Page,
instance::Instance, person::Person,
note::Note, tombstone::Tombstone,
page::Page,
person::Person,
tombstone::Tombstone,
},
tests::test_parse_lemmy_item,
}, },
tests::{test_json, test_parse_lemmy_item},
}; };
#[actix_rt::test] #[test]
async fn test_parse_objects_lemmy() { fn test_parse_objects_lemmy() {
test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json").unwrap(); test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json").unwrap();
test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json").unwrap(); test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json").unwrap();
test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json").unwrap(); test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json").unwrap();
@ -45,38 +41,37 @@ mod tests {
test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json").unwrap(); test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json").unwrap();
} }
#[actix_rt::test] #[test]
async fn test_parse_objects_pleroma() { fn test_parse_objects_pleroma() {
file_to_json_object::<WithContext<Person>>("assets/pleroma/objects/person.json").unwrap(); test_json::<Person>("assets/pleroma/objects/person.json").unwrap();
file_to_json_object::<WithContext<Note>>("assets/pleroma/objects/note.json").unwrap(); test_json::<Note>("assets/pleroma/objects/note.json").unwrap();
file_to_json_object::<WithContext<ChatMessage>>("assets/pleroma/objects/chat_message.json") test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json").unwrap();
.unwrap();
} }
#[actix_rt::test] #[test]
async fn test_parse_objects_smithereen() { fn test_parse_objects_smithereen() {
file_to_json_object::<WithContext<Person>>("assets/smithereen/objects/person.json").unwrap(); test_json::<Person>("assets/smithereen/objects/person.json").unwrap();
file_to_json_object::<Note>("assets/smithereen/objects/note.json").unwrap(); test_json::<Note>("assets/smithereen/objects/note.json").unwrap();
} }
#[actix_rt::test] #[test]
async fn test_parse_objects_mastodon() { fn test_parse_objects_mastodon() {
file_to_json_object::<WithContext<Person>>("assets/mastodon/objects/person.json").unwrap(); test_json::<Person>("assets/mastodon/objects/person.json").unwrap();
file_to_json_object::<WithContext<Note>>("assets/mastodon/objects/note.json").unwrap(); test_json::<Note>("assets/mastodon/objects/note.json").unwrap();
} }
#[actix_rt::test] #[test]
async fn test_parse_objects_lotide() { fn test_parse_objects_lotide() {
file_to_json_object::<WithContext<Group>>("assets/lotide/objects/group.json").unwrap(); test_json::<Group>("assets/lotide/objects/group.json").unwrap();
file_to_json_object::<WithContext<Person>>("assets/lotide/objects/person.json").unwrap(); test_json::<Person>("assets/lotide/objects/person.json").unwrap();
file_to_json_object::<WithContext<Note>>("assets/lotide/objects/note.json").unwrap(); test_json::<Note>("assets/lotide/objects/note.json").unwrap();
file_to_json_object::<WithContext<Page>>("assets/lotide/objects/page.json").unwrap(); test_json::<Page>("assets/lotide/objects/page.json").unwrap();
file_to_json_object::<WithContext<Tombstone>>("assets/lotide/objects/tombstone.json").unwrap(); test_json::<Tombstone>("assets/lotide/objects/tombstone.json").unwrap();
} }
#[actix_rt::test] #[test]
async fn test_parse_object_friendica() { fn test_parse_object_friendica() {
file_to_json_object::<WithContext<Person>>("assets/friendica/objects/person.json").unwrap(); test_json::<Person>("assets/friendica/objects/person.json").unwrap();
file_to_json_object::<WithContext<Note>>("assets/friendica/objects/note.json").unwrap(); test_json::<Note>("assets/friendica/objects/note.json").unwrap();
} }
} }

View file

@ -2,7 +2,7 @@ use crate::{
fetcher::post_or_comment::PostOrComment, fetcher::post_or_comment::PostOrComment,
mentions::Mention, mentions::Mention,
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost}, objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
protocol::{Source, Unparsed}, protocol::Source,
}; };
use activitystreams_kinds::object::NoteType; use activitystreams_kinds::object::NoteType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -30,16 +30,15 @@ pub struct Note {
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
pub(crate) content: String, pub(crate) content: String,
pub(crate) in_reply_to: ObjectId<PostOrComment>,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
#[serde(default)] #[serde(default)]
pub(crate) source: SourceCompat, pub(crate) source: SourceCompat,
pub(crate) in_reply_to: ObjectId<PostOrComment>,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(default)] #[serde(default)]
pub(crate) tag: Vec<Mention>, pub(crate) tag: Vec<Mention>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }
/// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work /// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{ImageObject, Source, Unparsed}, protocol::{ImageObject, Source},
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{ use lemmy_apub_lib::{
@ -30,10 +30,11 @@ pub struct Page {
pub(crate) attributed_to: ObjectId<ApubPerson>, pub(crate) attributed_to: ObjectId<ApubPerson>,
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
pub(crate) name: String,
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
pub(crate) name: String,
pub(crate) content: Option<String>, pub(crate) content: Option<String>,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
pub(crate) source: Option<Source>, pub(crate) source: Option<Source>,
@ -44,8 +45,6 @@ pub struct Page {
pub(crate) stickied: Option<bool>, pub(crate) stickied: Option<bool>,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }
impl Page { impl Page {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::person::ApubPerson, objects::person::ApubPerson,
protocol::{objects::Endpoints, ImageObject, Source, Unparsed}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey}; use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
@ -21,9 +21,14 @@ pub struct Person {
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UserTypes, pub(crate) kind: UserTypes,
pub(crate) id: ObjectId<ApubPerson>, pub(crate) id: ObjectId<ApubPerson>,
/// username, set at account creation and can never be changed /// username, set at account creation and usually fixed after that
pub(crate) preferred_username: String, pub(crate) preferred_username: String,
/// displayname (can be changed at any time) pub(crate) inbox: Url,
/// mandatory field in activitypub, lemmy currently serves an empty outbox
pub(crate) outbox: Url,
pub(crate) public_key: PublicKey,
/// displayname
pub(crate) name: Option<String>, pub(crate) name: Option<String>,
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) source: Option<Source>, pub(crate) source: Option<Source>,
@ -32,13 +37,7 @@ pub struct Person {
/// user banner /// user banner
pub(crate) image: Option<ImageObject>, pub(crate) image: Option<ImageObject>,
pub(crate) matrix_user_id: Option<String>, pub(crate) matrix_user_id: Option<String>,
pub(crate) inbox: Url,
/// mandatory field in activitypub, currently empty in lemmy
pub(crate) outbox: Url,
pub(crate) endpoints: Option<Endpoints>, pub(crate) endpoints: Option<Endpoints>,
pub(crate) public_key: PublicKey,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
} }