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>
126 lines
5.4 KiB
Python
126 lines
5.4 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from devplacepy.database import get_table, init_db
|
|
from devplacepy.database.sync import claim_link, load_candidates
|
|
from devplacepy.services.sync.options import SyncOptions
|
|
from devplacepy.services.sync.selection import Selector
|
|
from devplacepy.services.sync.spec import LIMITS, SKIP_MIN_COMMENTS
|
|
|
|
ORIGIN = "selection.example.org"
|
|
|
|
|
|
@pytest.fixture
|
|
def world(local_db):
|
|
init_db()
|
|
get_table("sync_candidates").delete()
|
|
get_table("sync_links").delete(origin=ORIGIN)
|
|
yield
|
|
get_table("sync_candidates").delete()
|
|
get_table("sync_links").delete(origin=ORIGIN)
|
|
|
|
|
|
def options(**extra):
|
|
return SyncOptions(min_comments=2, **extra)
|
|
|
|
|
|
def user(uid):
|
|
return {"origin_uid": uid, "username": f"user{uid}", "created_at": "2026-01-01T00:00:00Z"}
|
|
|
|
|
|
def post(uid, author="a"):
|
|
return {"origin_uid": uid, "author_uid": author, "content": f"post body {uid} long enough", "created_at": "2026-01-02T00:00:00Z"}
|
|
|
|
|
|
def comment(uid, on, author="b", parent=None):
|
|
record = {"origin_uid": uid, "post_uid": on, "author_uid": author, "content": f"comment {uid}", "created_at": "2026-01-03T00:00:00Z"}
|
|
if parent:
|
|
record["parent_uid"] = parent
|
|
return record
|
|
|
|
|
|
def run(records, persist=True, **extra):
|
|
selector = Selector(ORIGIN, options(**extra), persist=persist)
|
|
return selector, *selector.select(records)
|
|
|
|
|
|
def test_a_post_below_the_minimum_is_parked_with_its_comments_and_authors(world):
|
|
selector, kept, skipped = run({"users": [user("a"), user("b")], "posts": [post("p1")], "comments": [comment("c1", "p1")]})
|
|
assert kept["posts"] == [] and kept["comments"] == [] and skipped[SKIP_MIN_COMMENTS] == 1
|
|
assert selector.parked == 1
|
|
stored = json.loads(load_candidates(ORIGIN)[0]["record"])
|
|
assert stored["post"]["origin_uid"] == "p1" and [item["origin_uid"] for item in stored["comments"]] == ["c1"]
|
|
assert {item["origin_uid"] for item in stored["users"]} == {"a", "b"}
|
|
|
|
|
|
def test_comments_arriving_in_a_later_run_complete_the_post(world):
|
|
run({"users": [user("a"), user("b")], "posts": [post("p1")], "comments": [comment("c1", "p1")]})
|
|
selector, kept, skipped = run({"users": [user("b")], "posts": [], "comments": [comment("c2", "p1")]})
|
|
assert selector.revived == 1
|
|
assert [item["origin_uid"] for item in kept["posts"]] == ["p1"]
|
|
assert [item["origin_uid"] for item in kept["comments"]] == ["c1", "c2"]
|
|
assert {item["origin_uid"] for item in kept["users"]} == {"a", "b"}
|
|
assert not skipped and load_candidates(ORIGIN) == []
|
|
|
|
|
|
def test_a_repeated_comment_is_counted_once(world):
|
|
run({"users": [user("a"), user("b")], "posts": [post("p1")], "comments": [comment("c1", "p1")]})
|
|
selector, kept, skipped = run({"users": [], "posts": [], "comments": [comment("c1", "p1")]})
|
|
assert kept["posts"] == [] and skipped[SKIP_MIN_COMMENTS] == 1
|
|
assert len(load_candidates(ORIGIN)) == 1
|
|
|
|
|
|
def test_reply_order_is_kept_parent_before_reply(world):
|
|
run({"users": [user("a"), user("b")], "posts": [post("p1")], "comments": [comment("c1", "p1")]})
|
|
_, kept, _ = run({"users": [], "posts": [], "comments": [comment("c2", "p1", parent="c1")]})
|
|
assert [item["origin_uid"] for item in kept["comments"]] == ["c1", "c2"]
|
|
|
|
|
|
def test_a_dry_run_neither_reads_nor_writes_candidates(world):
|
|
selector, _, skipped = run({"users": [user("a")], "posts": [post("p1")], "comments": []}, persist=False)
|
|
assert skipped[SKIP_MIN_COMMENTS] == 1 and selector.parked == 0 and load_candidates(ORIGIN) == []
|
|
|
|
|
|
def test_a_known_post_is_never_parked_or_dropped(world):
|
|
claim_link(ORIGIN, "posts", "p9", "local-9", "x", "2026-01-01T00:00:00+00:00")
|
|
selector, kept, skipped = run({"users": [user("a")], "posts": [post("p9")], "comments": []})
|
|
assert [item["origin_uid"] for item in kept["posts"]] == ["p9"] and not skipped and selector.parked == 0
|
|
|
|
|
|
def test_the_candidate_list_is_capped_per_origin(world, monkeypatch):
|
|
from dataclasses import replace
|
|
|
|
from devplacepy.services.sync import selection
|
|
|
|
monkeypatch.setattr(selection, "LIMITS", replace(LIMITS, candidate_max_per_origin=2))
|
|
selector, _, _ = run({"users": [user("a")], "posts": [post("p1"), post("p2"), post("p3")], "comments": []})
|
|
assert selector.parked == 2 and len(load_candidates(ORIGIN)) == 2
|
|
|
|
|
|
def test_an_oversize_candidate_is_dropped_not_stored(world, monkeypatch):
|
|
from dataclasses import replace
|
|
|
|
from devplacepy.services.sync import selection
|
|
|
|
monkeypatch.setattr(selection, "LIMITS", replace(LIMITS, candidate_max_bytes=50))
|
|
selector, _, skipped = run({"users": [user("a")], "posts": [post("p1")], "comments": []})
|
|
assert skipped[SKIP_MIN_COMMENTS] == 1 and selector.parked == 0
|
|
|
|
|
|
def test_expired_candidates_are_not_revived(world):
|
|
run({"users": [user("a")], "posts": [post("p1")], "comments": []})
|
|
get_table("sync_candidates").update({"uid": load_candidates(ORIGIN)[0]["uid"], "expires_at": "2000-01-01T00:00:00+00:00"}, ["uid"])
|
|
selector, _, _ = run({"users": [], "posts": [], "comments": []})
|
|
assert selector.revived == 0 and load_candidates(ORIGIN) == []
|
|
|
|
|
|
def test_other_filters_stay_final_and_clear_the_candidate(world):
|
|
run({"users": [user("a")], "posts": [post("p1")], "comments": []})
|
|
selector, kept, skipped = run({"users": [user("a")], "posts": [], "comments": []}, topics=("devlog",))
|
|
from devplacepy.services.sync.spec import SKIP_TOPIC
|
|
|
|
assert kept["posts"] == [] and skipped[SKIP_TOPIC] == 1 and load_candidates(ORIGIN) == []
|