Repair production automation truth and schedule cleanup
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 18s

This commit is contained in:
tegwick 2026-08-20 11:20:23 +02:00
parent 8bcb416285
commit 944fd158de
19 changed files with 441 additions and 64 deletions

View file

@ -89,7 +89,7 @@ async def test_evaluate_instructions_returns_report_payload(monkeypatch) -> None
"trusted_fields": [],
"model": "test-model",
"prompt": "Run report.",
"output_schema": "schemas/daily-triage-report.json",
"output_schema": "activity-core://schemas/daily-triage-report.json",
"review_required": False,
}
],

View file

@ -201,6 +201,15 @@ async def test_status_endpoint_wraps_report(
}
monkeypatch.setattr(ops_api, "ops_status", fake_status)
session = AsyncMock()
session.__aenter__ = AsyncMock(return_value=session)
session.__aexit__ = AsyncMock(return_value=None)
count_result = MagicMock()
count_result.all.return_value = []
stuck_result = MagicMock()
stuck_result.scalar_one.return_value = 0
session.execute = AsyncMock(side_effect=[count_result, stuck_result])
ops_app.state.session_factory.return_value = session
transport = ASGITransport(app=ops_app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
res = await client.get("/ops/automations/status", params={"since": "sunday"})

View file

@ -79,6 +79,25 @@ def test_external_configmap_projects_disabled_ops_probe_definition(tmp_path) ->
]
def test_daily_todo_pause_note_matches_safe_sink_default(tmp_path) -> None:
runtime_config = _by_kind_name("ConfigMap", "actcore-runtime-config")
definition_config = _by_kind_name(
"ConfigMap", "actcore-external-activity-definitions"
)
raw_definition = definition_config["data"]["daily-todo-md-stale-review.md"]
definition_path = tmp_path / "daily-todo-md-stale-review.md"
definition_path.write_text(raw_definition, encoding="utf-8")
definition = parse_file(definition_path)
assert runtime_config["data"]["ISSUE_SINK_TYPE"] == "state-hub"
assert definition.enabled is False
assert "ACTIVITY-WP-0022" in raw_definition
assert "`ISSUE_SINK_TYPE=state-hub`" in raw_definition
assert "default `rest`" not in raw_definition
assert "does **not** create a Forgejo issue" in raw_definition
def test_external_configmap_projects_weekly_legacy_meter_review(tmp_path) -> None:
config = _by_kind_name("ConfigMap", "actcore-external-activity-definitions")
raw_definition = config["data"]["weekly-legacy-meter-review.md"]
@ -146,7 +165,7 @@ def test_statehub_edge_relay_deployment_uses_state_hub_image_and_outbox_pvc() ->
container = deployment["spec"]["template"]["spec"]["containers"][0]
env = {item["name"]: item["value"] for item in container["env"]}
assert container["image"] == "forgejo.coulomb.social/coulomb/state-hub:main-d8808bf"
assert container["image"] == "forgejo.coulomb.social/coulomb/state-hub:main-1cf949b"
assert env["STATEHUB_UPSTREAM_URL"] == "http://state-hub.state-hub.svc.cluster.local:8000"
assert env["STATEHUB_OUTBOX_PATH"] == "/var/statehub/edge-outbox.sqlite3"
assert env["STATEHUB_READ_CACHE_PATH"] == "/var/statehub/edge-read-cache.sqlite3"

View file

@ -76,6 +76,8 @@ def test_post_missed_fire_alert_dual_writes_workplan_scope(monkeypatch) -> None:
posts: list[dict] = []
class _Resp:
status_code = 201
def raise_for_status(self) -> None: ...
def json(self) -> dict[str, str]:
return {"id": "progress-1"}

View file

@ -38,6 +38,7 @@ async def test_sync_schedule_rows_reports_drift_counts_and_preserves_one_shots(
orphan_id = uuid.uuid4()
upserted: list[tuple[uuid.UUID, bool, str]] = []
deleted: list[str] = []
cancelled_one_shots: list[uuid.UUID] = []
async def fake_upsert_schedule(client: object, defn: object) -> None:
upserted.append((
@ -65,9 +66,13 @@ async def test_sync_schedule_rows_reports_drift_counts_and_preserves_one_shots(
async def fake_delete_schedule(client: object, activity_id: str) -> None:
deleted.append(activity_id)
async def fake_cancel_scheduled(client: object, activity_id: uuid.UUID) -> None:
cancelled_one_shots.append(activity_id)
monkeypatch.setattr(sync_schedules, "upsert_schedule", fake_upsert_schedule)
monkeypatch.setattr(sync_schedules, "list_schedules", fake_list_schedules)
monkeypatch.setattr(sync_schedules, "delete_schedule", fake_delete_schedule)
monkeypatch.setattr(sync_schedules, "cancel_scheduled", fake_cancel_scheduled)
result = await sync_schedules.sync_schedule_rows(
object(),
@ -125,6 +130,49 @@ async def test_sync_schedule_rows_reports_drift_counts_and_preserves_one_shots(
(one_shot_id, True, "scheduled"),
]
assert deleted == [str(orphan_id)]
assert cancelled_one_shots == []
@pytest.mark.asyncio
async def test_sync_schedule_rows_removes_disabled_one_shot(monkeypatch) -> None:
one_shot_id = uuid.uuid4()
cancelled: list[uuid.UUID] = []
async def fake_upsert_schedule(client: object, defn: object) -> None:
raise AssertionError("disabled one-shot must not be upserted")
async def fake_cancel_scheduled(client: object, activity_id: uuid.UUID) -> None:
cancelled.append(activity_id)
async def fake_list_schedules(client: object) -> list[dict[str, str]]:
return []
monkeypatch.setattr(sync_schedules, "upsert_schedule", fake_upsert_schedule)
monkeypatch.setattr(sync_schedules, "cancel_scheduled", fake_cancel_scheduled)
monkeypatch.setattr(sync_schedules, "list_schedules", fake_list_schedules)
result = await sync_schedules.sync_schedule_rows(
object(),
[
_row(
activity_id=one_shot_id,
enabled=False,
trigger_config={
"trigger_type": "scheduled",
"at": datetime(2026, 8, 17, 6, 0, tzinfo=timezone.utc),
"timezone": "UTC",
},
)
],
)
assert cancelled == [one_shot_id]
assert result.to_dict() == {
"upserted": 0,
"paused": 1,
"deleted_orphans": 0,
"errors": 0,
}
@pytest.mark.asyncio

View file

@ -52,6 +52,7 @@ async def test_run_sync_runs_requested_sections(monkeypatch) -> None:
"upserted": 3,
"paused": 1,
"deleted_orphans": 2,
"errors": 0,
}
assert result["errors"] == []