Files
devplacepy/tests/unit/services/community/alias.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

135 lines
5.0 KiB
Python

# retoor <retoor@molodetz.nl>
import pytest
from devplacepy.database import get_setting, get_table, init_db, set_setting
from devplacepy.services.community.runner import TRIGGER_CLI, CommunityRunner
from devplacepy.services.community.store import store
from devplacepy.services.sync import ai
from devplacepy.services.sync.options import ALIAS_KEY, SyncOptions
from tests.conftest import run_async
from tests.unit.services.community.fakes import FakeFetcher, json_community, no_sleep
ORIGINALS = ("writer0", "writer1", "replier0", "replier1")
@pytest.fixture
def world(monkeypatch):
init_db()
keys = ("sync_enabled", "sync_origins", ALIAS_KEY)
previous = {key: get_setting(key, "") for key in keys}
set_setting("sync_enabled", "1")
set_setting("sync_origins", "")
set_setting(ALIAS_KEY, "1")
for table in ("communities", "community_runs"):
get_table(table).delete()
get_table("sync_links").delete(origin="forum.test")
get_table("sync_identities").delete(origin="forum.test")
async def unreachable(system, text, timeout):
return None
monkeypatch.setattr(ai, "complete", unreachable)
runner = CommunityRunner(fetcher_factory=lambda: FakeFetcher(json_community()), sleep=no_sleep)
store.create("forum-test", "https://forum.test/api/posts", "tester", name="forum")
run_async(runner.discover("https://forum.test/api/posts", "tester", relearn_slug="forum-test"))
yield runner
for key, value in previous.items():
set_setting(key, value)
get_table("sync_identities").delete(origin="forum.test")
def go(runner, dry_run=False):
return run_async(runner.run(store.get("forum-test"), dry_run=dry_run, trigger=TRIGGER_CLI))
def synced(kind: str) -> list[dict]:
links = get_table("sync_links").find(origin="forum.test", kind=kind)
return [row for row in (get_table(kind).find_one(uid=link["local_uid"]) for link in links) if row]
def local_text() -> str:
names = [row["username"] for row in synced("users")]
posts = [f"{row['title']} {row['content']}" for row in synced("posts")]
comments = [row["content"] for row in synced("comments")]
return " ".join(names + posts + comments).lower()
def enable(**changes):
row = store.get("forum-test")
store.update(row["uid"], sync_options=SyncOptions.from_row(row).merged(**changes).to_dict())
def test_aliasing_off_by_default_creates_no_identities_and_derives_from_originals(world):
report = go(world)
assert report.status == "ok", report.errors
assert report.pushed["users"].get("created", 0) == 4
assert get_table("sync_identities").count(origin="forum.test") == 0
assert "writer" in local_text().replace("wr1ter", "writer")
def test_alias_run_never_stores_an_original_username(world):
enable(alias=True)
report = go(world)
assert report.status == "ok", report.errors
text = local_text()
assert not [name for name in ORIGINALS if name in text]
assert get_table("sync_identities").count(origin="forum.test") == 4
again = go(world)
assert again.status == "ok"
assert again.pushed["users"].get("created", 0) == 0
assert get_table("sync_identities").count(origin="forum.test") == 4
def test_kill_switch_stops_aliased_runs_and_creates_nothing(world):
enable(alias=True)
set_setting(ALIAS_KEY, "0")
report = go(world)
assert any("switched off" in error for error in report.errors)
assert not synced("posts") and not synced("users")
assert get_table("sync_identities").count(origin="forum.test") == 0
def test_dry_run_creates_no_identities_and_no_rows(world):
enable(alias=True)
users = get_table("users").count()
report = go(world, dry_run=True)
assert report.status == "dry_run", report.errors
assert get_table("sync_identities").count(origin="forum.test") == 0
assert get_table("users").count() == users
def test_enabling_alias_after_unaliased_sync_is_refused_at_run(world):
assert go(world).status == "ok"
enable(alias=True)
report = go(world)
assert any("already has synced records" in error for error in report.errors)
assert get_table("sync_identities").count(origin="forum.test") == 0
def test_selection_skips_are_reported_and_sampling_is_stable(world):
enable(sample_percent=30)
first = go(world)
count = len(synced("posts"))
assert first.pushed["skipped"]["sampled_out"] + count >= 10
assert 0 < count < 10
go(world)
assert len(synced("posts")) == count
def test_translation_failure_holds_watermark_and_stores_nothing(world):
enable(alias=True, translate_to="nl")
row = store.get("forum-test")
store.update(row["uid"], language="en")
report = go(world)
assert not synced("posts")
assert report.status == "partial"
assert report.pushed["skipped"]["translation_failed"] >= 1
assert not store.get("forum-test")["watermark"]
def test_jitter_moves_next_run_within_bounds(world):
row = store.get("forum-test")
moments = {store.schedule_next(row["uid"], 86400, 40) for _ in range(5)}
assert len(moments) > 1