Files
devplacepy/tests/api/sync/pending.py
T
retoorandClaude Sonnet 5.5 02ab747cd0 Code Farm: per-plot auto-replant pause and crop-lock check; community sync and node work
Continuous Delivery could never be turned off once a crop was planted. Add a
one-shot per-plot "stop after harvest" flag (POST /game/plot/stop, Devii tool
game_plot_stop, game_plots.stop_after_harvest), cleared by the next plant,
autoreplant claim or Refactor. Autoreplant now also respects crop locks through
the shared _crop_lock predicate. Plan and measurements in happyplanting.md.

Also includes the pending community sync, node network and related work in the
tree; the full suite passed (4999) with all of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 02:23:43 +02:00

121 lines
6.5 KiB
Python

# retoor <retoor@molodetz.nl>
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