Activity receive() tests (src/tests/activities.rs):
- Accept: updates following_status to Accepted with correct user/actor
- Reject: removes following with correct user/actor
- Undo(Follow): removes follower + calls on_actor_removed
- Undo(Like): calls on_unlike for local objects; ignores remote objects
- Undo(Announce): removes announce record + calls on_announce_removed for local;
removes record but skips notification for remote objects
- Create: uses object["id"] not activity id; mention fires on_mention + on_create
- Update: uses object["id"]
- Delete(object): calls on_delete; does NOT call on_actor_removed
- Delete(actor): calls on_actor_removed; does NOT call on_delete
- Announce(local): records announce + calls on_announce_received
- Announce(remote): calls on_announce_of_remote; does NOT record announce
- Like(local): calls on_like
- Like(remote): silently ignored
- Add: uses object["id"] not activity id
- Block: removes both following and follower
- Domain block: activity skipped before any processing
- Actor block: Follow skipped before HTTP dereference (SSRF fix)
- Idempotency: duplicate delivery skipped
Actor serialization tests (src/tests/actors.rs):
- actor_type=Service serializes as "Service"
- discoverable=false serializes
- also_known_as serializes as JSON array (all aliases, not just first)
- optional fields omitted when None
- featured URL serialized when set
Visibility addressing tests (src/tests/broadcast.rs):
- Public: to=[AS_PUBLIC], cc=[followers]
- FollowersOnly: to=[followers], cc=[] — AS_PUBLIC absent
- Private: both empty
143 lines
5.1 KiB
Rust
143 lines
5.1 KiB
Rust
use super::*;
|
|
|
|
// ── Person AP JSON serialization ──────────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn person_serializes_with_enriched_fields() {
|
|
let person = Person {
|
|
kind: Default::default(),
|
|
id: "https://example.com/users/1"
|
|
.parse::<url::Url>()
|
|
.unwrap()
|
|
.into(),
|
|
preferred_username: "alice".to_string(),
|
|
inbox: "https://example.com/users/1/inbox".parse().unwrap(),
|
|
outbox: Some("https://example.com/users/1/outbox".parse().unwrap()),
|
|
followers: Some("https://example.com/users/1/followers".parse().unwrap()),
|
|
following: Some("https://example.com/users/1/following".parse().unwrap()),
|
|
public_key: activitypub_federation::protocol::public_key::PublicKey {
|
|
id: "https://example.com/users/1#main-key".to_string(),
|
|
owner: "https://example.com/users/1".parse().unwrap(),
|
|
public_key_pem: "pem".to_string(),
|
|
},
|
|
name: Some("Alice".to_string()),
|
|
summary: Some("Bio text".to_string()),
|
|
icon: Some(ApImageObject {
|
|
kind: "Image".to_string(),
|
|
url: "https://example.com/images/avatars/1".parse().unwrap(),
|
|
}),
|
|
url: Some("https://example.com/u/alice".parse().unwrap()),
|
|
discoverable: Some(true),
|
|
manually_approves_followers: true,
|
|
updated: Some(chrono::Utc::now()),
|
|
endpoints: Some(Endpoints {
|
|
shared_inbox: "https://example.com/inbox".parse().unwrap(),
|
|
}),
|
|
image: None,
|
|
also_known_as: vec![],
|
|
attachment: vec![],
|
|
featured: Some("https://example.com/users/1/featured".parse().unwrap()),
|
|
};
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert_eq!(json["discoverable"], true);
|
|
assert_eq!(json["summary"], "Bio text");
|
|
assert_eq!(json["icon"]["type"], "Image");
|
|
assert_eq!(json["manuallyApprovesFollowers"], true);
|
|
assert!(json.get("updated").is_some());
|
|
assert!(json.get("endpoints").is_some());
|
|
assert_eq!(
|
|
json["endpoints"]["sharedInbox"],
|
|
"https://example.com/inbox"
|
|
);
|
|
assert_eq!(json["featured"], "https://example.com/users/1/featured");
|
|
}
|
|
|
|
#[test]
|
|
fn person_actor_type_service_serializes_correctly() {
|
|
let mut person = minimal_person();
|
|
person.kind = crate::user::ApActorType::Service;
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert_eq!(json["type"], "Service");
|
|
}
|
|
|
|
#[test]
|
|
fn person_discoverable_false_serializes() {
|
|
let mut person = minimal_person();
|
|
person.discoverable = Some(false);
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert_eq!(json["discoverable"], false);
|
|
}
|
|
|
|
#[test]
|
|
fn person_also_known_as_serializes_as_array() {
|
|
let mut person = minimal_person();
|
|
person.also_known_as = vec![
|
|
"https://old.example/users/alice".to_string(),
|
|
"https://other.example/users/alice".to_string(),
|
|
];
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert!(
|
|
json["alsoKnownAs"].is_array(),
|
|
"alsoKnownAs must serialize as a JSON array"
|
|
);
|
|
assert_eq!(json["alsoKnownAs"].as_array().unwrap().len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn person_omits_optional_fields_when_none() {
|
|
let person = minimal_person();
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert!(
|
|
json.get("summary").is_none(),
|
|
"null summary should be omitted"
|
|
);
|
|
assert!(json.get("icon").is_none(), "null icon should be omitted");
|
|
assert!(
|
|
json.get("featured").is_none(),
|
|
"null featured should be omitted"
|
|
);
|
|
assert!(json.get("url").is_none(), "null url should be omitted");
|
|
}
|
|
|
|
#[test]
|
|
fn person_featured_omitted_when_none() {
|
|
let mut person = minimal_person();
|
|
person.featured = None;
|
|
let json = serde_json::to_value(&person).unwrap();
|
|
assert!(json.get("featured").is_none());
|
|
}
|
|
|
|
// ── helper ────────────────────────────────────────────────────────────────────
|
|
|
|
fn minimal_person() -> Person {
|
|
Person {
|
|
kind: Default::default(),
|
|
id: "https://example.com/users/1"
|
|
.parse::<url::Url>()
|
|
.unwrap()
|
|
.into(),
|
|
preferred_username: "alice".to_string(),
|
|
inbox: "https://example.com/users/1/inbox".parse().unwrap(),
|
|
outbox: None,
|
|
followers: None,
|
|
following: None,
|
|
public_key: activitypub_federation::protocol::public_key::PublicKey {
|
|
id: "https://example.com/users/1#main-key".to_string(),
|
|
owner: "https://example.com/users/1".parse().unwrap(),
|
|
public_key_pem: "pem".to_string(),
|
|
},
|
|
name: None,
|
|
summary: None,
|
|
icon: None,
|
|
url: None,
|
|
discoverable: None,
|
|
manually_approves_followers: false,
|
|
updated: None,
|
|
endpoints: None,
|
|
image: None,
|
|
also_known_as: vec![],
|
|
attachment: vec![],
|
|
featured: None,
|
|
}
|
|
}
|