61 lines
2.6 KiB
Python
61 lines
2.6 KiB
Python
from pathlib import Path
|
|
import sys
|
|
import pytest
|
|
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0,str(ROOT/"active-runtime/app"))
|
|
|
|
from aios_online_agent_execution import GovernedExecutionBinding
|
|
from aios_online_agent_plan_store import ExecutionPlanStore, ExecutionPlanError
|
|
|
|
BASE="a642b045775b9154a00ae78543037fbef4fb447b"
|
|
HEAD="71523aef6e48b1e2fa516c456ca189b7a039104b"
|
|
|
|
def binding(head=HEAD):
|
|
return GovernedExecutionBinding.from_mapping({
|
|
"task_id":"task-plan-a","job_id":"job-plan-a","repository":"aios/aios","base_oid":BASE,
|
|
"expected_branch_oid":head,"target_branch":"agent/task-plan-a","branch_mode":"CONTINUE_EXISTING",
|
|
"generation":1,"idempotency_key":"governed-plan-a",
|
|
})
|
|
|
|
def request():
|
|
return {
|
|
"schema":"aios.online-agent.plan.v1","task_id":"task-plan-a","job_id":"job-plan-a",
|
|
"source_head_oid":HEAD,"generation":1,"idempotency_key":"plan-task-a-v1",
|
|
"execution":{
|
|
"action":"deterministic",
|
|
"change_set":[{"path":"x.py","operation":"write_file","content":"x=1\n"}],
|
|
"verification":[{"label":"compile","argv":["/usr/bin/python3","-m","py_compile","x.py"],"cwd":".","timeout_seconds":30}],
|
|
"capability_scope":["git.read","git.test"],
|
|
}
|
|
}
|
|
|
|
def test_plan_register_and_resolve(tmp_path):
|
|
store=ExecutionPlanStore(tmp_path)
|
|
created=store.register(binding(),request())
|
|
assert created["idempotent"] is False
|
|
resolved=store.resolve(binding())
|
|
assert resolved["plan_digest"]==created["plan_digest"]
|
|
assert resolved["execution"]["action"]=="deterministic"
|
|
|
|
def test_plan_is_idempotent(tmp_path):
|
|
store=ExecutionPlanStore(tmp_path); req=request()
|
|
first=store.register(binding(),req); second=store.register(binding(),req)
|
|
assert second["idempotent"] is True
|
|
assert first["plan_digest"]==second["plan_digest"]
|
|
|
|
def test_plan_source_head_drift_denied(tmp_path):
|
|
store=ExecutionPlanStore(tmp_path); store.register(binding(),request())
|
|
with pytest.raises(ExecutionPlanError,match="SOURCE_HEAD_DRIFT"):
|
|
store.resolve(binding("b"*40))
|
|
|
|
def test_plan_replacement_requires_cas(tmp_path):
|
|
store=ExecutionPlanStore(tmp_path); first=store.register(binding(),request())
|
|
changed=request(); changed["idempotency_key"]="plan-task-a-v2"
|
|
changed["execution"]["change_set"][0]["content"]="x=2\n"
|
|
with pytest.raises(ExecutionPlanError,match="REPLACEMENT_CAS_MISMATCH"):
|
|
store.register(binding(),changed)
|
|
changed["expected_previous_plan_digest"]=first["plan_digest"]
|
|
second=store.register(binding(),changed)
|
|
assert second["plan_digest"]!=first["plan_digest"]
|