test: move activitypub tests to tests/
Assert via serialized JSON (fields pub(crate)); normalize_hashtag covered via review tags. No inline exceptions.
This commit is contained in:
@@ -306,7 +306,3 @@ pub fn goal_to_ap_object(
|
|||||||
cc: vec![format!("{}/followers", actor_url)],
|
cc: vec![format!("{}/followers", actor_url)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[path = "tests/objects.rs"]
|
|
||||||
mod tests;
|
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn normalize_hashtag_strips_non_alphanumeric() {
|
|
||||||
assert_eq!(normalize_hashtag("The Dark Knight"), "TheDarkKnight");
|
|
||||||
assert_eq!(normalize_hashtag("Schindler's List"), "SchindlersList");
|
|
||||||
assert_eq!(
|
|
||||||
normalize_hashtag("2001: A Space Odyssey"),
|
|
||||||
"2001ASpaceOdyssey"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn review_to_ap_object_includes_two_hashtags() {
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use domain::{
|
|
||||||
models::{PersistedReview, Review, ReviewSource},
|
|
||||||
value_objects::{MovieId, Rating, ReviewId, UserId},
|
|
||||||
};
|
|
||||||
|
|
||||||
let review = Review::from_persistence(PersistedReview {
|
|
||||||
id: ReviewId::generate(),
|
|
||||||
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
user_id: UserId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
rating: Rating::new(4).unwrap(),
|
|
||||||
comment: None,
|
|
||||||
watched_at: NaiveDateTime::parse_from_str("2024-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
.unwrap(),
|
|
||||||
created_at: NaiveDateTime::parse_from_str("2024-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
.unwrap(),
|
|
||||||
source: ReviewSource::Local,
|
|
||||||
watch_medium: None,
|
|
||||||
spoilers: false,
|
|
||||||
});
|
|
||||||
let obj = review_to_ap_object(
|
|
||||||
&review,
|
|
||||||
ReviewApInput {
|
|
||||||
ap_id: "https://example.com/reviews/1".parse().unwrap(),
|
|
||||||
actor_url: "https://example.com/users/1".parse().unwrap(),
|
|
||||||
movie_title: "Dune".to_string(),
|
|
||||||
release_year: 2021,
|
|
||||||
external_metadata_id: None,
|
|
||||||
poster_url: None,
|
|
||||||
base_url: "https://example.com".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert_eq!(obj.tag.len(), 2);
|
|
||||||
let names: Vec<&str> = obj.tag.iter().map(|t| t.name.as_str()).collect();
|
|
||||||
assert!(names.contains(&"#MoviesDiary"));
|
|
||||||
assert!(names.contains(&"#Dune"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn review_to_ap_object_has_public_addressing() {
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use domain::{
|
|
||||||
models::{PersistedReview, Review, ReviewSource},
|
|
||||||
value_objects::{MovieId, Rating, ReviewId, UserId},
|
|
||||||
};
|
|
||||||
|
|
||||||
let review = Review::from_persistence(PersistedReview {
|
|
||||||
id: ReviewId::generate(),
|
|
||||||
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
user_id: UserId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
rating: Rating::new(3).unwrap(),
|
|
||||||
comment: None,
|
|
||||||
watched_at: NaiveDateTime::parse_from_str("2024-06-01 00:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
.unwrap(),
|
|
||||||
created_at: NaiveDateTime::parse_from_str("2024-06-01 00:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
.unwrap(),
|
|
||||||
source: ReviewSource::Local,
|
|
||||||
watch_medium: None,
|
|
||||||
spoilers: false,
|
|
||||||
});
|
|
||||||
let actor_url: url::Url = "https://example.com/users/abc".parse().unwrap();
|
|
||||||
let obj = review_to_ap_object(
|
|
||||||
&review,
|
|
||||||
ReviewApInput {
|
|
||||||
ap_id: "https://example.com/reviews/1".parse().unwrap(),
|
|
||||||
actor_url: actor_url.clone(),
|
|
||||||
movie_title: "Dune".to_string(),
|
|
||||||
release_year: 2021,
|
|
||||||
external_metadata_id: None,
|
|
||||||
poster_url: None,
|
|
||||||
base_url: "https://example.com".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert_eq!(obj.to, vec!["https://www.w3.org/ns/activitystreams#Public"]);
|
|
||||||
assert_eq!(obj.cc, vec!["https://example.com/users/abc/followers"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn review_to_ap_object_carries_spoiler_flag_and_summary() {
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use domain::{
|
|
||||||
models::{PersistedReview, Review, ReviewSource},
|
|
||||||
value_objects::{Comment, MovieId, Rating, ReviewId, UserId},
|
|
||||||
};
|
|
||||||
|
|
||||||
let ts = NaiveDateTime::parse_from_str("2024-06-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
|
|
||||||
let review = Review::from_persistence(PersistedReview {
|
|
||||||
id: ReviewId::generate(),
|
|
||||||
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
user_id: UserId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
rating: Rating::new(4).unwrap(),
|
|
||||||
comment: Some(Comment::new("the mentor is the villain".into()).unwrap()),
|
|
||||||
watched_at: ts,
|
|
||||||
created_at: ts,
|
|
||||||
source: ReviewSource::Local,
|
|
||||||
watch_medium: None,
|
|
||||||
spoilers: true,
|
|
||||||
});
|
|
||||||
let obj = review_to_ap_object(
|
|
||||||
&review,
|
|
||||||
ReviewApInput {
|
|
||||||
ap_id: "https://example.com/reviews/1".parse().unwrap(),
|
|
||||||
actor_url: "https://example.com/users/1".parse().unwrap(),
|
|
||||||
movie_title: "Dune".to_string(),
|
|
||||||
release_year: 2021,
|
|
||||||
external_metadata_id: None,
|
|
||||||
poster_url: None,
|
|
||||||
base_url: "https://example.com".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert!(obj.contains_spoilers);
|
|
||||||
assert_eq!(obj.summary.as_deref(), Some("Contains spoilers"));
|
|
||||||
let json = serde_json::to_value(&obj).unwrap();
|
|
||||||
assert_eq!(json["containsSpoilers"], serde_json::Value::Bool(true));
|
|
||||||
assert_eq!(json["summary"], "Contains spoilers");
|
|
||||||
|
|
||||||
// A flag without a comment never travels: there is nothing to veil.
|
|
||||||
let review_no_comment = Review::from_persistence(PersistedReview {
|
|
||||||
id: ReviewId::generate(),
|
|
||||||
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
user_id: UserId::from_uuid(uuid::Uuid::new_v4()),
|
|
||||||
rating: Rating::new(4).unwrap(),
|
|
||||||
comment: None,
|
|
||||||
watched_at: ts,
|
|
||||||
created_at: ts,
|
|
||||||
source: ReviewSource::Local,
|
|
||||||
watch_medium: None,
|
|
||||||
spoilers: true,
|
|
||||||
});
|
|
||||||
let obj2 = review_to_ap_object(
|
|
||||||
&review_no_comment,
|
|
||||||
ReviewApInput {
|
|
||||||
ap_id: "https://example.com/reviews/2".parse().unwrap(),
|
|
||||||
actor_url: "https://example.com/users/1".parse().unwrap(),
|
|
||||||
movie_title: "Dune".to_string(),
|
|
||||||
release_year: 2021,
|
|
||||||
external_metadata_id: None,
|
|
||||||
poster_url: None,
|
|
||||||
base_url: "https://example.com".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert!(!obj2.contains_spoilers);
|
|
||||||
assert!(obj2.summary.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn watchlist_to_ap_object_has_public_addressing() {
|
|
||||||
let actor_url: url::Url = "https://example.com/users/abc".parse().unwrap();
|
|
||||||
let obj = watchlist_to_ap_object(WatchlistApInput {
|
|
||||||
ap_id: "https://example.com/watchlist/1".parse().unwrap(),
|
|
||||||
actor_url: actor_url.clone(),
|
|
||||||
movie_title: "Alien".to_string(),
|
|
||||||
release_year: 1979,
|
|
||||||
external_metadata_id: None,
|
|
||||||
poster_url: None,
|
|
||||||
added_at: chrono::Utc::now(),
|
|
||||||
base_url: "https://example.com".to_string(),
|
|
||||||
});
|
|
||||||
assert_eq!(obj.to, vec!["https://www.w3.org/ns/activitystreams#Public"]);
|
|
||||||
assert_eq!(obj.cc, vec!["https://example.com/users/abc/followers"]);
|
|
||||||
}
|
|
||||||
4
crates/adapters/activitypub/tests/objects.rs
Normal file
4
crates/adapters/activitypub/tests/objects.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#[path = "objects/review_object_test.rs"]
|
||||||
|
mod review_object_test;
|
||||||
|
#[path = "objects/watchlist_object_test.rs"]
|
||||||
|
mod watchlist_object_test;
|
||||||
112
crates/adapters/activitypub/tests/objects/review_object_test.rs
Normal file
112
crates/adapters/activitypub/tests/objects/review_object_test.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
use activitypub::objects::{ReviewApInput, review_to_ap_object};
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use domain::models::{PersistedReview, Review, ReviewSource};
|
||||||
|
use domain::value_objects::{Comment, MovieId, Rating, ReviewId, UserId};
|
||||||
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
|
const PUBLIC: &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
|
||||||
|
fn timestamp() -> NaiveDateTime {
|
||||||
|
NaiveDateTime::parse_from_str("2024-06-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn review(rating: u8, comment: Option<&str>, spoilers: bool) -> Review {
|
||||||
|
Review::from_persistence(PersistedReview {
|
||||||
|
id: ReviewId::generate(),
|
||||||
|
movie_id: MovieId::from_uuid(uuid::Uuid::new_v4()),
|
||||||
|
user_id: UserId::from_uuid(uuid::Uuid::new_v4()),
|
||||||
|
rating: Rating::new(rating).unwrap(),
|
||||||
|
comment: comment.map(|c| Comment::new(c.into()).unwrap()),
|
||||||
|
watched_at: timestamp(),
|
||||||
|
created_at: timestamp(),
|
||||||
|
source: ReviewSource::Local,
|
||||||
|
watch_medium: None,
|
||||||
|
spoilers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input(movie_title: &str, actor_url: &str) -> ReviewApInput {
|
||||||
|
ReviewApInput {
|
||||||
|
ap_id: "https://example.com/reviews/1".parse().unwrap(),
|
||||||
|
actor_url: actor_url.parse().unwrap(),
|
||||||
|
movie_title: movie_title.to_string(),
|
||||||
|
release_year: 2021,
|
||||||
|
external_metadata_id: None,
|
||||||
|
poster_url: None,
|
||||||
|
base_url: "https://example.com".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(review: &Review, input: ReviewApInput) -> Value {
|
||||||
|
serde_json::to_value(review_to_ap_object(review, input)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag_names(json: &Value) -> Vec<&str> {
|
||||||
|
json["tag"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|t| t["name"].as_str().unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn review_object_tags_movie_title_with_non_alphanumerics_stripped() {
|
||||||
|
for (title, hashtag) in [
|
||||||
|
("The Dark Knight", "#TheDarkKnight"),
|
||||||
|
("Schindler's List", "#SchindlersList"),
|
||||||
|
("2001: A Space Odyssey", "#2001ASpaceOdyssey"),
|
||||||
|
] {
|
||||||
|
let json = render(
|
||||||
|
&review(4, None, false),
|
||||||
|
input(title, "https://example.com/users/1"),
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
tag_names(&json).contains(&hashtag),
|
||||||
|
"{title}: {:?}",
|
||||||
|
tag_names(&json)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn review_object_includes_two_hashtags() {
|
||||||
|
let json = render(
|
||||||
|
&review(4, None, false),
|
||||||
|
input("Dune", "https://example.com/users/1"),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(tag_names(&json), vec!["#MoviesDiary", "#Dune"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn review_object_is_publicly_addressed_with_followers_in_cc() {
|
||||||
|
let json = render(
|
||||||
|
&review(3, None, false),
|
||||||
|
input("Dune", "https://example.com/users/abc"),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(json["to"], json!([PUBLIC]));
|
||||||
|
assert_eq!(
|
||||||
|
json["cc"],
|
||||||
|
json!(["https://example.com/users/abc/followers"])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn review_object_carries_spoiler_flag_and_summary_only_with_a_comment() {
|
||||||
|
let json = render(
|
||||||
|
&review(4, Some("the mentor is the villain"), true),
|
||||||
|
input("Dune", "https://example.com/users/1"),
|
||||||
|
);
|
||||||
|
assert_eq!(json["containsSpoilers"], Value::Bool(true));
|
||||||
|
assert_eq!(json["summary"], "Contains spoilers");
|
||||||
|
|
||||||
|
// A flag without a comment never travels: there is nothing to veil.
|
||||||
|
let json = render(
|
||||||
|
&review(4, None, true),
|
||||||
|
input("Dune", "https://example.com/users/1"),
|
||||||
|
);
|
||||||
|
assert!(json.get("containsSpoilers").is_none());
|
||||||
|
assert!(json.get("summary").is_none());
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
use activitypub::objects::{WatchlistApInput, watchlist_to_ap_object};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn watchlist_object_is_publicly_addressed_with_followers_in_cc() {
|
||||||
|
let obj = watchlist_to_ap_object(WatchlistApInput {
|
||||||
|
ap_id: "https://example.com/watchlist/1".parse().unwrap(),
|
||||||
|
actor_url: "https://example.com/users/abc".parse().unwrap(),
|
||||||
|
movie_title: "Alien".to_string(),
|
||||||
|
release_year: 1979,
|
||||||
|
external_metadata_id: None,
|
||||||
|
poster_url: None,
|
||||||
|
added_at: chrono::Utc::now(),
|
||||||
|
base_url: "https://example.com".to_string(),
|
||||||
|
});
|
||||||
|
let json = serde_json::to_value(&obj).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
json["to"],
|
||||||
|
json!(["https://www.w3.org/ns/activitystreams#Public"])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
json["cc"],
|
||||||
|
json!(["https://example.com/users/abc/followers"])
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user