# retoor import dataclasses import random from devplacepy.database import get_table from devplacepy.services.sync import processors from devplacepy.services.sync.pending import PendingQueue from devplacepy.services.sync.spec import LIMITS from tests.api.sync.conftest import comment_record, post_record, sync_ok, unique, user_record def link_count(origin, kind): return get_table("sync_links").count(origin=origin, kind=kind) def test_a_chain_of_waiting_records_is_released_by_the_first_dependency(client, admin, origin): reply = sync_ok(client, admin, "comments", origin, [comment_record("c2", "p1", "u1", parent_uid="c1")])["results"][0] parent = sync_ok(client, admin, "comments", origin, [comment_record("c1", "p1", "u1")])["results"][0] post = sync_ok(client, admin, "posts", origin, [post_record("p1", "u1")])["results"][0] assert (reply["status"], parent["status"], post["status"]) == ("deferred", "deferred", "deferred") assert get_table("sync_pending").count(origin=origin) == 3 body = sync_ok(client, admin, "users", origin, [user_record("u1", unique("chain"))]) assert body["results"][0]["status"] == "created" assert body["released"] == {"created": 3} assert get_table("sync_pending").count(origin=origin) == 0 assert [link_count(origin, kind) for kind in ("users", "posts", "comments")] == [1, 1, 2] comments = {row["origin_uid"]: row for row in get_table("sync_links").find(origin=origin, kind="comments")} reply_row = get_table("comments").find_one(uid=comments["c2"]["local_uid"]) assert reply_row["parent_uid"] == comments["c1"]["local_uid"] def test_any_arrival_order_ends_in_the_same_final_state(client, admin, origin): records = [ ("users", user_record("u1", unique("order1"))), ("users", user_record("u2", unique("order2"))), ("posts", post_record("p1", "u1")), *[("comments", comment_record(f"c{index}", "p1", "u2")) for index in range(15)], ] random.Random(7).shuffle(records) for kind, record in records: sync_ok(client, admin, kind, origin, [record]) assert [link_count(origin, kind) for kind in ("users", "posts", "comments")] == [2, 1, 15] assert get_table("sync_pending").count(origin=origin) == 0 def test_a_resend_replaces_the_waiting_row_instead_of_stacking(client, admin, origin): for _ in range(3): sync_ok(client, admin, "posts", origin, [post_record("p1", "ghost")]) assert get_table("sync_pending").count(origin=origin) == 1 def test_a_resend_carries_the_newest_content(client, admin, origin): sync_ok(client, admin, "posts", origin, [post_record("p1", "u1", content="the first version of it")]) sync_ok(client, admin, "posts", origin, [post_record("p1", "u1", content="the second version of it", updated_at="2025-02-01T00:00:00Z")]) sync_ok(client, admin, "users", origin, [user_record("u1", unique("newest"))]) link = get_table("sync_links").find_one(origin=origin, kind="posts", origin_uid="p1") assert get_table("posts").find_one(uid=link["local_uid"])["content"] == "the second version of it" def test_the_waiting_list_is_capped_per_origin_and_says_so(client, admin, origin, monkeypatch): monkeypatch.setattr(processors, "LIMITS", dataclasses.replace(LIMITS, pending_max_per_origin=2)) body = sync_ok(client, admin, "posts", origin, [post_record(f"p{index}", "ghost") for index in range(4)]) assert [item["status"] for item in body["results"]] == ["deferred", "deferred", "rejected", "rejected"] assert "waiting list" in body["results"][2]["errors"][-1] assert get_table("sync_pending").count(origin=origin) == 2 def test_the_cap_is_per_origin(client, admin, origin, monkeypatch): monkeypatch.setattr(processors, "LIMITS", dataclasses.replace(LIMITS, pending_max_per_origin=1)) other = f"{unique('o')}.example.org" sync_ok(client, admin, "posts", origin, [post_record("p1", "ghost")]) assert sync_ok(client, admin, "posts", other, [post_record("p1", "ghost")])["results"][0]["status"] == "deferred" def test_a_dry_run_never_queues(client, admin, origin): body = sync_ok(client, admin, "posts", origin, [post_record("p1", "ghost")], dry_run=True) assert body["results"][0]["status"] == "rejected" and body["released"] == {} assert get_table("sync_pending").count(origin=origin) == 0 def test_a_tombstone_for_an_unknown_record_is_not_queued(client, admin, origin): body = sync_ok(client, admin, "comments", origin, [comment_record("c1", "nopost", "nobody", deleted=True)]) assert body["results"][0]["status"] == "unchanged" and body["results"][0]["reason"] == "tombstone_for_unknown" assert get_table("sync_pending").count(origin=origin) == 0 def test_an_oversize_record_is_not_queued(client, admin, origin, monkeypatch): monkeypatch.setattr(processors, "LIMITS", dataclasses.replace(LIMITS, pending_max_record_bytes=100)) result = sync_ok(client, admin, "posts", origin, [post_record("p1", "ghost")])["results"][0] assert result["status"] == "rejected" and "too large" in result["errors"][-1] def test_expired_and_exhausted_rows_are_dropped_hard(client, admin, origin): sync_ok(client, admin, "posts", origin, [post_record("p1", "ghost"), post_record("p2", "ghost")]) rows = list(get_table("sync_pending").find(origin=origin)) get_table("sync_pending").update({"uid": rows[0]["uid"], "expires_at": "2000-01-01T00:00:00+00:00"}, ["uid"]) get_table("sync_pending").update({"uid": rows[1]["uid"], "attempts": LIMITS.pending_max_attempts}, ["uid"]) assert PendingQueue().sweep() >= 2 assert get_table("sync_pending").count(origin=origin) == 0 def test_the_release_budget_bounds_the_work_per_call(client, admin, origin): for index in range(5): sync_ok(client, admin, "posts", origin, [post_record(f"p{index}", "u1")]) from devplacepy.database.sync import claim_link author = get_table("users").insert({"uid": "budget-author", "username": unique("budget"), "created_at": "2025-01-01T00:00:00+00:00"}) claim_link(origin, "users", "u1", "budget-author", "x", "2025-01-01T00:00:00+00:00") released = PendingQueue().release(origin, cap=2) assert sum(released.values()) == 2 and author assert get_table("sync_pending").count(origin=origin) == 3 def test_pending_rows_of_one_origin_are_never_released_by_another(client, admin, origin): other = f"{unique('o')}.example.org" sync_ok(client, admin, "posts", origin, [post_record("p1", "u1")]) sync_ok(client, admin, "users", other, [user_record("u1", unique("isolated"))]) assert get_table("sync_pending").count(origin=origin) == 1 assert link_count(origin, "posts") == 0