generated from coulomb/repo-seed
Add kaizen context resolver for scheduled agent fleet discovery.
Implement discover_kaizen_scheduled_repos and discover_kaizen_projects per kaizen-agentic ADR-005 contract: State Hub roster, roster.yaml filter, schedule validation, and prepare_command emission. Register kaizen/resolver/shell source types with unit tests and runbook dry-run instructions.
This commit is contained in:
195
tests/test_kaizen_context_resolver.py
Normal file
195
tests/test_kaizen_context_resolver.py
Normal file
@@ -0,0 +1,195 @@
|
||||
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, {}) == {}
|
||||
Reference in New Issue
Block a user