generated from coulomb/repo-seed
Implement post-triage operational hardening
This commit is contained in:
56
tests/test_daily_triage_verifier.py
Normal file
56
tests/test_daily_triage_verifier.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _load_script():
|
||||
path = Path(__file__).parent.parent / "scripts" / "verify_daily_triage.py"
|
||||
spec = importlib.util.spec_from_file_location("verify_daily_triage", path)
|
||||
assert spec is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_daily_triage_verifier_dry_run_names_all_operator_checks() -> None:
|
||||
script = _load_script()
|
||||
args = script.parse_args([
|
||||
"--activity-id",
|
||||
"00000000-0000-0000-0000-000000000123",
|
||||
"--date",
|
||||
"2026-06-04",
|
||||
"--working-memory-dir",
|
||||
"/tmp/wm",
|
||||
])
|
||||
|
||||
report = script.build_dry_run_report(args)
|
||||
|
||||
assert report["mode"] == "dry-run"
|
||||
names = {check["name"] for check in report["checks"]}
|
||||
assert names == {
|
||||
"temporal_schedule",
|
||||
"latest_workflow_history",
|
||||
"activity_runs_row",
|
||||
"state_hub_progress",
|
||||
"working_memory_note",
|
||||
"llm_timeout_budget",
|
||||
}
|
||||
assert report["activity"]["schedule_id"] == (
|
||||
"activity-schedule-00000000-0000-0000-0000-000000000123"
|
||||
)
|
||||
assert any(
|
||||
check.get("path_glob") == "/tmp/wm/daily-triage-2026-06-04-*.md"
|
||||
for check in report["checks"]
|
||||
)
|
||||
timeout_check = next(
|
||||
check for check in report["checks"] if check["name"] == "llm_timeout_budget"
|
||||
)
|
||||
run_check = next(
|
||||
check for check in report["checks"] if check["name"] == "activity_runs_row"
|
||||
)
|
||||
assert "activity_runs.activity_id" in run_check["sql"]
|
||||
assert "where id = '00000000-0000-0000-0000-000000000123'" in timeout_check["sql"]
|
||||
assert timeout_check["activity_timeout_seconds"] == 900
|
||||
assert timeout_check["retry_attempts"] == 10
|
||||
126
tests/test_issue_sink.py
Normal file
126
tests/test_issue_sink.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from activity_core import activities
|
||||
from activity_core.issue_sink import IssueCoreRestSink
|
||||
from activity_core.rules.models import TaskRef, TaskSpec
|
||||
|
||||
|
||||
class DummyResponse:
|
||||
def __init__(self, payload: dict[str, Any]) -> None:
|
||||
self.payload = payload
|
||||
|
||||
def raise_for_status(self) -> None:
|
||||
return None
|
||||
|
||||
def json(self) -> dict[str, Any]:
|
||||
return self.payload
|
||||
|
||||
|
||||
def test_issue_core_rest_sink_posts_task_contract(monkeypatch) -> None:
|
||||
posts: list[dict[str, Any]] = []
|
||||
|
||||
def fake_post(url: str, **kwargs: Any) -> DummyResponse:
|
||||
posts.append({"url": url, **kwargs})
|
||||
return DummyResponse({
|
||||
"issue_id": "issue-123",
|
||||
"issue_url": "http://issue-core.test/issues/issue-123",
|
||||
"backend": "issue-core",
|
||||
})
|
||||
|
||||
monkeypatch.setattr(httpx, "post", fake_post)
|
||||
|
||||
ref = IssueCoreRestSink("http://issue-core.test/").emit(TaskSpec(
|
||||
title="Run SBOM rescan for activity-core",
|
||||
description="SBOM is older than 30 days.",
|
||||
target_repo="activity-core",
|
||||
priority="medium",
|
||||
labels=["sbom", "security", "automated"],
|
||||
due_in_days=7,
|
||||
source_type="rule",
|
||||
source_id="flag-stale-sbom",
|
||||
triggering_event_id="scheduled",
|
||||
activity_definition_id="activity-1",
|
||||
))
|
||||
|
||||
assert ref == TaskRef(
|
||||
external_id="issue-123",
|
||||
backend_url="http://issue-core.test/issues/issue-123",
|
||||
backend="issue-core",
|
||||
)
|
||||
assert posts == [
|
||||
{
|
||||
"url": "http://issue-core.test/issues/",
|
||||
"json": {
|
||||
"title": "Run SBOM rescan for activity-core",
|
||||
"description": "SBOM is older than 30 days.",
|
||||
"target_repo": "activity-core",
|
||||
"priority": "medium",
|
||||
"labels": ["sbom", "security", "automated"],
|
||||
"due_in_days": 7,
|
||||
"source_type": "rule",
|
||||
"source_id": "flag-stale-sbom",
|
||||
"triggering_event_id": "scheduled",
|
||||
"activity_definition_id": "activity-1",
|
||||
},
|
||||
"timeout": 10.0,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_emit_tasks_raises_when_sink_fails(monkeypatch) -> None:
|
||||
class FailingSink:
|
||||
def emit(self, task_spec: TaskSpec) -> TaskRef:
|
||||
raise RuntimeError(f"boom for {task_spec.title}")
|
||||
|
||||
class FakeTransaction:
|
||||
async def __aenter__(self) -> None:
|
||||
return None
|
||||
|
||||
async def __aexit__(self, *exc_info: object) -> bool:
|
||||
return False
|
||||
|
||||
class FakeSession:
|
||||
def begin(self) -> FakeTransaction:
|
||||
return FakeTransaction()
|
||||
|
||||
async def __aenter__(self) -> "FakeSession":
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *exc_info: object) -> bool:
|
||||
return False
|
||||
|
||||
def add(self, row: object) -> None:
|
||||
raise AssertionError("failed emissions should not write spawn logs")
|
||||
|
||||
class FakeSessionFactory:
|
||||
def __call__(self) -> FakeSession:
|
||||
return FakeSession()
|
||||
|
||||
monkeypatch.setattr(activities, "get_issue_sink", lambda: FailingSink())
|
||||
monkeypatch.setattr(activities, "_get_session_factory", lambda: FakeSessionFactory())
|
||||
|
||||
with pytest.raises(RuntimeError, match="task emission sink failure"):
|
||||
await activities.emit_tasks({
|
||||
"activity_id": "00000000-0000-0000-0000-000000000001",
|
||||
"triggering_event_id": "scheduled",
|
||||
"run_id": "00000000-0000-0000-0000-000000000002",
|
||||
"task_specs": [
|
||||
{
|
||||
"title": "Run SBOM rescan for activity-core",
|
||||
"description": "",
|
||||
"target_repo": "activity-core",
|
||||
"priority": "medium",
|
||||
"labels": ["sbom"],
|
||||
"due_in_days": None,
|
||||
"source_type": "rule",
|
||||
"source_id": "flag-stale-sbom",
|
||||
"condition": "context.repo.sbom_age_days > 30",
|
||||
}
|
||||
],
|
||||
})
|
||||
@@ -235,7 +235,7 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None:
|
||||
payloads = {
|
||||
"/state/summary": {
|
||||
"generated_at": "2026-05-19T05:20:00Z",
|
||||
"totals": {"tasks": {"todo": 4, "blocked": 1}},
|
||||
"totals": {"tasks": {"todo": 4, "wait": 1}},
|
||||
"topics": [
|
||||
{
|
||||
"slug": "custodian",
|
||||
@@ -306,7 +306,7 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None:
|
||||
{
|
||||
"id": "task-2",
|
||||
"title": "T06 - Canary Cutover",
|
||||
"status": "blocked",
|
||||
"status": "wait",
|
||||
"priority": "medium",
|
||||
"needs_human": True,
|
||||
},
|
||||
@@ -331,13 +331,13 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None:
|
||||
|
||||
import json
|
||||
digest = json.loads(raw_digest)
|
||||
assert digest["totals"] == {"tasks": {"todo": 4, "blocked": 1}}
|
||||
assert digest["totals"] == {"tasks": {"todo": 4, "wait": 1}}
|
||||
assert digest["open_workstreams"][0]["slug"] == "cust-wp-0045"
|
||||
assert digest["open_workstreams"][0]["planning_priority"] == "high"
|
||||
assert digest["open_workstreams"][0]["open_task_counts"] == {
|
||||
"wait": 1,
|
||||
"todo": 1,
|
||||
"in_progress": 0,
|
||||
"blocked": 1,
|
||||
"progress": 0,
|
||||
"needs_human": 1,
|
||||
"open_total": 2,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user