Files

106 lines
3.9 KiB
Python
Raw Permalink Normal View History

from pathlib import Path
import pytest
from pydantic import ValidationError
from gewu.application.acceptance import ActorContext
from gewu.application.runs import LocalRunService, RunError
from gewu.domain.contracts import (
RunCheckpointRequest,
RunCreateRequest,
RunModulePin,
RunState,
Scope,
)
def _create(run_id: str = "run-pilot") -> RunCreateRequest:
return RunCreateRequest(
run_id=run_id,
objective="Compare audience response",
scope=Scope(projects=("dome-performance",), include_shared=False),
lens="Audience behavior",
pack_id="gewu",
pack_version="0.3.0",
pack_snapshot_digest="a" * 64,
module_pins=(
RunModulePin(
module_id="profile",
implementation_version="1.0.0",
sha256="b" * 64,
),
),
)
def _checkpoint(run_id: str = "run-pilot", **overrides: object) -> RunCheckpointRequest:
values: dict[str, object] = {
"run_id": run_id,
"expected_revision": 0,
"stage": "source-review",
"state": RunState.ACTIVE,
"budgets": {"source_count": 3},
}
values.update(overrides)
return RunCheckpointRequest.model_validate(values)
def test_run_create_and_checkpoint_are_idempotent_and_revision_checked(tmp_path: Path) -> None:
current = True
actor = ActorContext("agent-pilot", "pilot-key")
service = LocalRunService(
workflow_root=tmp_path / "workflow",
actor_is_current=lambda _: current,
)
created = service.create(_create(), actor=actor)
assert created.revision == 0
assert service.create(_create(), actor=actor) == created
with pytest.raises(RunError, match="different input"):
service.create(_create().model_copy(update={"objective": "Different"}), actor=actor)
checkpoint = _checkpoint()
updated = service.checkpoint(checkpoint, actor=actor)
assert updated.revision == 1
assert service.checkpoint(checkpoint, actor=actor) == updated
with pytest.raises(RunError, match="changed content"):
service.checkpoint(_checkpoint(stage="different-stage"), actor=actor)
service.checkpoint(_checkpoint(expected_revision=1, stage="next-stage"), actor=actor)
with pytest.raises(RunError, match="stale"):
service.checkpoint(_checkpoint(expected_revision=0, stage="different-stage"), actor=actor)
assert service.status("run-pilot", actor=actor).revision == 2
def test_run_owner_revocation_and_terminal_state_are_enforced(tmp_path: Path) -> None:
active = True
owner = ActorContext("agent-pilot", "pilot-key")
other = ActorContext("agent-other", "other-key")
service = LocalRunService(
workflow_root=tmp_path / "workflow",
actor_is_current=lambda actor: active and actor.credential_id in {"pilot-key", "other-key"},
)
service.create(_create(), actor=owner)
with pytest.raises(RunError, match="unavailable"):
service.status("run-pilot", actor=other)
completed = service.checkpoint(_checkpoint(state=RunState.COMPLETED), actor=owner)
assert completed.state is RunState.COMPLETED
with pytest.raises(RunError, match="cannot be updated"):
service.checkpoint(_checkpoint(expected_revision=1), actor=owner)
active = False
with pytest.raises(RunError, match="no longer active"):
service.checkpoint(_checkpoint(expected_revision=1), actor=owner)
with pytest.raises(RunError, match="no longer active"):
service.status("run-pilot", actor=owner)
def test_run_contract_rejects_global_scope_and_negative_budgets() -> None:
with pytest.raises(ValidationError):
RunCreateRequest.model_validate(
_create().model_dump() | {"scope": Scope(projects=("all",), all_projects=True)}
)
with pytest.raises(ValidationError):
RunCheckpointRequest.model_validate(
{"run_id": "run-pilot", "expected_revision": 0, "stage": "x", "budgets": {"tokens": -1}}
)