DevPlace CI / test (push) Failing after 44m45s
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>
110 lines
6.0 KiB
Python
110 lines
6.0 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from devplacepy.database import get_table
|
|
from tests.api.sync.conftest import comment_record, post_record, sync_ok, unique, user_record
|
|
|
|
|
|
def listing(client, admin, origin, **params):
|
|
return client.get("/sync/deletions", params={"origin": origin, **params}, headers=admin["headers"])
|
|
|
|
|
|
def ack(client, admin, origin, items):
|
|
return client.post("/sync/deletions/ack", json={"origin": origin, "items": items}, headers=admin["headers"])
|
|
|
|
|
|
def seed(client, admin, origin):
|
|
sync_ok(client, admin, "users", origin, [user_record("u1", unique("del"))])
|
|
sync_ok(client, admin, "posts", origin, [post_record("p1", "u1"), post_record("p2", "u1"), post_record("p3", "u1")])
|
|
sync_ok(client, admin, "comments", origin, [comment_record("c1", "p1", "u1")])
|
|
|
|
|
|
def local(origin, kind, origin_uid):
|
|
return get_table("sync_links").find_one(origin=origin, kind=kind, origin_uid=origin_uid)["local_uid"]
|
|
|
|
|
|
def test_authz(client, admin):
|
|
from tests.api.sync.conftest import make_account
|
|
|
|
assert client.get("/sync/deletions", params={"origin": "a.b"}).status_code == 401
|
|
assert listing(client, make_account("Member"), "a.b").status_code == 403
|
|
assert client.get("/sync/deletions", headers=admin["headers"]).status_code == 422
|
|
|
|
|
|
def test_nothing_is_listed_for_a_clean_origin_and_sync_own_deletions_never_are(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
assert listing(client, admin, origin).json()["items"] == []
|
|
sync_ok(client, admin, "posts", origin, [post_record("p1", "u1", updated_at="2025-05-01T00:00:00Z", deleted=True)])
|
|
assert listing(client, admin, origin).json()["items"] == []
|
|
|
|
|
|
def test_a_member_deletion_a_system_deletion_and_a_purge_are_told_apart(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
get_table("posts").update({"uid": local(origin, "posts", "p1"), "deleted_at": "2026-01-01T00:00:00+00:00", "deleted_by": "someone"}, ["uid"])
|
|
get_table("posts").update({"uid": local(origin, "posts", "p2"), "deleted_at": "2026-01-02T00:00:00+00:00", "deleted_by": "system"}, ["uid"])
|
|
purged_uid = local(origin, "posts", "p3")
|
|
get_table("posts").delete(uid=purged_uid)
|
|
body = listing(client, admin, origin).json()
|
|
found = {(item["kind"], item["origin_uid"]): item["by"] for item in body["items"]}
|
|
assert found == {("posts", "p1"): "member", ("posts", "p2"): "system", ("posts", "p3"): "purged"}
|
|
assert body["count"] == 3 and body["has_more"] is False
|
|
|
|
|
|
def test_paging_with_a_cursor_covers_every_entry_once(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
for origin_uid in ("p1", "p2", "p3"):
|
|
get_table("posts").update({"uid": local(origin, "posts", origin_uid), "deleted_at": "2026-01-01T00:00:00+00:00", "deleted_by": "m"}, ["uid"])
|
|
get_table("comments").update({"uid": local(origin, "comments", "c1"), "deleted_at": "2026-01-01T00:00:00+00:00", "deleted_by": "m"}, ["uid"])
|
|
seen, cursor = [], ""
|
|
while True:
|
|
body = listing(client, admin, origin, limit=2, cursor=cursor).json()
|
|
seen += [(item["kind"], item["origin_uid"]) for item in body["items"]]
|
|
cursor = body["next_cursor"]
|
|
if not body["has_more"]:
|
|
break
|
|
assert sorted(seen) == [("comments", "c1"), ("posts", "p1"), ("posts", "p2"), ("posts", "p3")]
|
|
assert listing(client, admin, origin, cursor="zz").status_code == 400
|
|
|
|
|
|
def test_other_origins_are_never_listed(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
get_table("posts").update({"uid": local(origin, "posts", "p1"), "deleted_at": "2026-01-01T00:00:00+00:00", "deleted_by": "m"}, ["uid"])
|
|
assert listing(client, admin, f"{unique('x')}.example.org").json()["items"] == []
|
|
|
|
|
|
def test_acknowledging_stops_the_listing_and_quiets_the_resend(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
purged = local(origin, "posts", "p3")
|
|
get_table("posts").delete(uid=purged)
|
|
resend = sync_ok(client, admin, "posts", origin, [post_record("p3", "u1", updated_at="2025-09-01T00:00:00Z")])["results"][0]
|
|
assert resend["status"] == "rejected" and resend["reason"] == "purged_locally"
|
|
response = ack(client, admin, origin, [{"kind": "posts", "origin_uid": "p3"}])
|
|
assert response.status_code == 200 and response.json()["acknowledged"] == 1
|
|
assert listing(client, admin, origin).json()["items"] == []
|
|
again = sync_ok(client, admin, "posts", origin, [post_record("p3", "u1", updated_at="2025-10-01T00:00:00Z")])["results"][0]
|
|
assert again["status"] == "unchanged" and again["reason"] == "acknowledged_deleted"
|
|
assert ack(client, admin, origin, [{"kind": "posts", "origin_uid": "p3"}]).json()["acknowledged"] == 0
|
|
|
|
|
|
def test_a_locally_deleted_row_after_ack_answers_acknowledged(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
get_table("posts").update({"uid": local(origin, "posts", "p1"), "deleted_at": "2026-01-01T00:00:00+00:00", "deleted_by": "m"}, ["uid"])
|
|
ack(client, admin, origin, [{"kind": "posts", "origin_uid": "p1"}])
|
|
result = sync_ok(client, admin, "posts", origin, [post_record("p1", "u1", content="edited after local delete", updated_at="2025-09-01T00:00:00Z")])["results"][0]
|
|
assert result["status"] == "unchanged" and result["reason"] == "acknowledged_deleted"
|
|
|
|
|
|
def test_ack_validation_and_audit(client, admin, origin):
|
|
assert ack(client, admin, origin, []).status_code == 422
|
|
assert ack(client, admin, origin, [{"kind": "gists", "origin_uid": "x"}]).status_code == 422
|
|
assert ack(client, admin, origin, [{"kind": "posts", "origin_uid": "bad id"}]).status_code == 422
|
|
assert ack(client, admin, origin, [{"kind": "posts", "origin_uid": "unknown-one"}]).json()["acknowledged"] == 0
|
|
rows = list(get_table("audit_log").find(event_key="sync.deletions.ack", actor_uid=admin["uid"]))
|
|
assert rows
|
|
|
|
|
|
def test_a_purged_user_is_listed(client, admin, origin):
|
|
seed(client, admin, origin)
|
|
get_table("users").delete(uid=local(origin, "users", "u1"))
|
|
items = listing(client, admin, origin).json()["items"]
|
|
assert ("users", "u1", "purged") in {(item["kind"], item["origin_uid"], item["by"]) for item in items}
|