# retoor 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_keeps_original_names(world): report = go(world) assert report.status == "ok", report.errors assert "writer0" in local_text() assert get_table("sync_identities").count(origin="forum.test") == 0 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