from __future__ import annotations from pathlib import Path from typing import Any import httpx import pytest import yaml from activity_core.context_resolvers.kaizen import ( KaizenContextResolver, discover_kaizen_scheduled_repos, ) class DummyResponse: def __init__(self, payload: Any, status_error: Exception | None = None) -> None: self.payload = payload self.status_error = status_error def raise_for_status(self) -> None: if self.status_error is not None: raise self.status_error def json(self) -> Any: return self.payload def _write_schedule(path: Path, agents: dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text( yaml.safe_dump( {"version": "1", "timezone": "Europe/Berlin", "agents": agents}, sort_keys=False, ), encoding="utf-8", ) def test_discover_scheduled_repos_emits_enabled_coach(tmp_path, monkeypatch) -> None: repo_root = tmp_path / "pilot-repo" repo_root.mkdir() _write_schedule( repo_root / ".kaizen" / "schedule.yml", {"coach": {"cadence": "daily", "cron": "15 * * * *", "enabled": True}}, ) def fake_get(url: str, **kwargs: Any) -> DummyResponse: return DummyResponse( [ { "slug": "pilot-repo", "domain_slug": "custodian", "host_paths": {"testhost": str(repo_root)}, } ] ) monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") monkeypatch.setenv("KAIZEN_RUNNER_HOST", "testhost") monkeypatch.setattr(httpx, "get", fake_get) result = discover_kaizen_scheduled_repos({}) assert len(result["scheduled_runs"]) == 1 run = result["scheduled_runs"][0] assert run["repo"] == "pilot-repo" assert run["agent"] == "coach" assert run["enabled"] is True assert "schedule prepare coach" in run["prepare_command"] def test_discover_scheduled_repos_skips_disabled_coach(tmp_path, monkeypatch) -> None: repo_root = tmp_path / "pilot-repo" repo_root.mkdir() _write_schedule( repo_root / ".kaizen" / "schedule.yml", {"coach": {"cadence": "daily", "enabled": False}}, ) monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") monkeypatch.setenv("KAIZEN_RUNNER_HOST", "testhost") monkeypatch.setattr( httpx, "get", lambda url, **kwargs: DummyResponse( [{"slug": "pilot-repo", "host_paths": {"testhost": str(repo_root)}}] ), ) result = discover_kaizen_scheduled_repos({}) assert result["scheduled_runs"] == [] def test_discover_scheduled_repos_skips_missing_schedule(tmp_path, monkeypatch) -> None: repo_root = tmp_path / "no-schedule" repo_root.mkdir() monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") monkeypatch.setenv("KAIZEN_RUNNER_HOST", "testhost") monkeypatch.setattr( httpx, "get", lambda url, **kwargs: DummyResponse( [{"slug": "no-schedule", "host_paths": {"testhost": str(repo_root)}}] ), ) result = discover_kaizen_scheduled_repos({}) assert result["scheduled_runs"] == [] def test_discover_scheduled_repos_skips_invalid_schedule(tmp_path, monkeypatch) -> None: repo_root = tmp_path / "bad-schedule" schedule = repo_root / ".kaizen" / "schedule.yml" schedule.parent.mkdir(parents=True) schedule.write_text("version: '2'\nagents: {}\n", encoding="utf-8") monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") monkeypatch.setenv("KAIZEN_RUNNER_HOST", "testhost") monkeypatch.setattr( httpx, "get", lambda url, **kwargs: DummyResponse( [{"slug": "bad-schedule", "host_paths": {"testhost": str(repo_root)}}] ), ) result = discover_kaizen_scheduled_repos({}) assert result["scheduled_runs"] == [] def test_discover_scheduled_repos_filters_by_roster_and_cadence( tmp_path, monkeypatch ) -> None: repo_a = tmp_path / "kaizen-agentic" repo_b = tmp_path / "other-repo" for root in (repo_a, repo_b): _write_schedule( root / ".kaizen" / "schedule.yml", { "coach": {"cadence": "daily", "enabled": True}, "optimization": {"cadence": "weekly", "enabled": True}, }, ) roster = tmp_path / "roster.yaml" roster.write_text( yaml.safe_dump( { "active": [ {"slug": "kaizen-agentic", "agents": ["coach"], "status": "active"} ] } ), encoding="utf-8", ) monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") monkeypatch.setenv("KAIZEN_RUNNER_HOST", "testhost") monkeypatch.setattr( httpx, "get", lambda url, **kwargs: DummyResponse( [ {"slug": "kaizen-agentic", "host_paths": {"testhost": str(repo_a)}}, {"slug": "other-repo", "host_paths": {"testhost": str(repo_b)}}, ] ), ) result = discover_kaizen_scheduled_repos( {"roster": str(roster), "cadence": "daily"} ) agents = {r["agent"] for r in result["scheduled_runs"]} repos = {r["repo"] for r in result["scheduled_runs"]} assert repos == {"kaizen-agentic"} assert agents == {"coach"} def test_hub_unreachable_raises(monkeypatch) -> None: monkeypatch.setenv("STATE_HUB_URL", "http://hub.test") def fail_get(url: str, **kwargs: Any) -> DummyResponse: raise httpx.ConnectError("down") monkeypatch.setattr(httpx, "get", fail_get) with pytest.raises(RuntimeError, match="State Hub unreachable"): discover_kaizen_scheduled_repos({}) def test_resolver_registry_alias() -> None: resolver = KaizenContextResolver() assert resolver.resolve("unknown_query", None, {}) == {}