Files
devplacepy/tests/unit/services/sync/selection.py
T
retoorandClaude Sonnet 5.5 02ab747cd0
DevPlace CI / test (push) Failing after 44m45s
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

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) == []