From cf62dae2c9aedcfe1e2617af415fb9ace1cccef3 Mon Sep 17 00:00:00 2001 From: fabi Date: Tue, 16 Jun 2026 13:38:15 +0000 Subject: [PATCH] test(crawler): deflake sync serialization concurrency test (#7) --- backend/tests/crawler_sync.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/tests/crawler_sync.rs b/backend/tests/crawler_sync.rs index 4425cdd..f7fbfa5 100644 --- a/backend/tests/crawler_sync.rs +++ b/backend/tests/crawler_sync.rs @@ -583,10 +583,18 @@ async fn sync_chapters_serializes_concurrent_calls_for_same_manga(pool: PgPool) .fetch_all(&pool) .await .unwrap(); - assert_eq!( - alive, - vec!["A".to_string(), "B".to_string(), "C".to_string()], - "all chapters survive concurrent syncs that both contain them" + // The per-manga advisory lock serializes the two calls, but it cannot + // dictate WHICH commits last (spawn order is not guaranteed — under load + // it inverts), so two correct last-writer-wins outcomes are possible: + // - Y (list [A,B,C]) commits last → nothing dropped → [A,B,C]. + // - X (list [A,B]) commits last → it soft-drops C, a key it never saw + // and which the next full sync re-adds → [A,B]. + // The lock's job is to prevent a TORN result (a lost B, a half-applied + // drop), not to fix an order it cannot. Accept either serialization; + // anything else means the lock failed to serialize. + assert!( + alive == ["A", "B", "C"] || alive == ["A", "B"], + "concurrent syncs must leave a valid serialization, got {alive:?}" ); }