Files
movies-diary/crates/bootstrap/tests/account_purge_test.rs
Gabriel Kaszewski 017b58d6fa
Some checks failed
CI / Check / Test (push) Has been cancelled
code refactor
Reviewed-on: #26
2026-10-26 09:36:23 +00:00

124 lines
4.1 KiB
Rust

mod support;
use std::time::Duration;
use domain::{events::DomainEvent, ports::UserRepository, value_objects::UserId};
async fn wait_for(pool: &sqlx::SqlitePool, sql: &str) -> bool {
for _ in 0..100 {
let n: i64 = sqlx::query_scalar(sql).fetch_one(pool).await.unwrap();
if n > 0 {
return true;
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
false
}
#[tokio::test]
async fn the_worker_purges_a_deleted_account_and_frees_its_email() {
let (router, pool) = support::server().await;
let alice = support::register(&router, "alice", "alice@example.com").await;
let id = UserId::from_uuid(uuid::Uuid::parse_str(&alice.user_id).unwrap());
let users = sqlite::SqliteUserRepository::new(pool.clone());
users
.mark_deleted(
&id,
&[DomainEvent::UserDeleted {
user_id: id.clone(),
}],
)
.await
.unwrap();
let worker = bootstrap::build_worker_on(&support::test_config(), pool.clone())
.await
.unwrap();
let (stop, stopped) = tokio::sync::watch::channel(false);
let running = tokio::spawn(worker.run(stopped));
let purged = wait_for(
&pool,
"SELECT COUNT(*) FROM users WHERE username = 'alice' AND purged_at IS NOT NULL",
)
.await;
stop.send(true).unwrap();
running.await.unwrap().unwrap();
assert!(purged, "the worker should purge the tombstoned account");
let email: String = sqlx::query_scalar("SELECT email FROM users WHERE username = 'alice'")
.fetch_one(&pool)
.await
.unwrap();
assert_ne!(email, "alice@example.com");
let again = support::register(&router, "alice2", "alice@example.com").await;
assert!(!again.user_id.is_empty(), "the email can register again");
}
#[cfg(feature = "federation")]
#[tokio::test]
async fn followers_are_told_before_the_purge_and_the_keypair_survives_it() {
let (router, pool) = support::server().await;
let alice = support::register(&router, "alice", "alice@example.com").await;
let id = UserId::from_uuid(uuid::Uuid::parse_str(&alice.user_id).unwrap());
for sql in [
"INSERT INTO ap_remote_actors (url, handle, inbox_url, fetched_at)
VALUES ('http://127.0.0.1:1/users/ann', 'ann@127.0.0.1', 'http://127.0.0.1:1/inbox', 'now')",
] {
sqlx::query(sql).execute(&pool).await.unwrap();
}
sqlx::query(
"INSERT INTO ap_followers (local_user_id, remote_actor_url, status, created_at)
VALUES (?, 'http://127.0.0.1:1/users/ann', 'accepted', 'now')",
)
.bind(&alice.user_id)
.execute(&pool)
.await
.unwrap();
let users = sqlite::SqliteUserRepository::new(pool.clone());
users
.mark_deleted(
&id,
&[DomainEvent::UserDeleted {
user_id: id.clone(),
}],
)
.await
.unwrap();
let worker = bootstrap::build_worker_on(&support::test_config(), pool.clone())
.await
.unwrap();
let (stop, stopped) = tokio::sync::watch::channel(false);
let running = tokio::spawn(worker.run(stopped));
let purged = wait_for(
&pool,
"SELECT COUNT(*) FROM users WHERE username = 'alice' AND purged_at IS NOT NULL",
)
.await;
let enqueued = wait_for(
&pool,
"SELECT COUNT(*) FROM event_queue WHERE event_type = 'FederationDeliveryRequested'",
)
.await;
stop.send(true).unwrap();
running.await.unwrap().unwrap();
assert!(purged);
assert!(enqueued, "the Delete should be enqueued for the follower");
let deliveries: i64 = sqlx::query_scalar(
"SELECT COUNT(*) FROM event_queue WHERE event_type = 'FederationDeliveryRequested'",
)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(deliveries, 1, "the Delete was enqueued for the follower");
let keys: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM ap_local_actors WHERE user_id = ?")
.bind(&alice.user_id)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(keys, 1, "the keypair stays until the tombstone cleanup");
}