feat: idempotent handlers, deterministic activity ids
This commit is contained in:
@@ -51,3 +51,75 @@ impl EventHandler for FollowBackfillHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use domain::models::FollowedActorInfo;
|
||||
use domain::value_objects::UserId;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Models the real port: imports are keyed upserts and backfill sends carry
|
||||
/// deterministic activity ids, so a repeat lands on the same key.
|
||||
#[derive(Default)]
|
||||
struct KeyedBackfill {
|
||||
imported: Mutex<HashSet<(String, String)>>,
|
||||
sent: Mutex<HashSet<(Uuid, String)>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ApBackfillPort for KeyedBackfill {
|
||||
async fn get_following(&self, _: Uuid) -> Result<Vec<FollowedActorInfo>, DomainError> {
|
||||
Ok(vec![FollowedActorInfo {
|
||||
url: "https://remote.example/users/a".into(),
|
||||
outbox_url: Some("https://remote.example/users/a/outbox".into()),
|
||||
}])
|
||||
}
|
||||
async fn import_remote_outbox(&self, outbox: &str, actor: &str) -> Result<(), DomainError> {
|
||||
self.imported
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert((outbox.into(), actor.into()));
|
||||
Ok(())
|
||||
}
|
||||
async fn run_backfill_for_follower(
|
||||
&self,
|
||||
owner: Uuid,
|
||||
inbox: String,
|
||||
) -> Result<(), DomainError> {
|
||||
self.sent.lock().unwrap().insert((owner, inbox));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn follow_backfill_is_idempotent_when_run_twice() {
|
||||
let port = Arc::new(KeyedBackfill::default());
|
||||
let handler = FollowBackfillHandler {
|
||||
backfill: port.clone(),
|
||||
};
|
||||
let owner = UserId::from_uuid(Uuid::new_v4());
|
||||
let accepted = DomainEvent::FollowAccepted {
|
||||
owner: owner.clone(),
|
||||
requester: SocialIdentity::Remote {
|
||||
actor_url: "https://remote.example/users/a".into(),
|
||||
},
|
||||
};
|
||||
let backfill = DomainEvent::BackfillFollower {
|
||||
owner_user_id: owner,
|
||||
follower_inbox_url: "https://remote.example/inbox".into(),
|
||||
};
|
||||
|
||||
for event in [&accepted, &backfill] {
|
||||
handler.handle(event).await.unwrap();
|
||||
handler.handle(event).await.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(port.imported.lock().unwrap().len(), 1);
|
||||
assert_eq!(port.sent.lock().unwrap().len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user