Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m38s
CD Pipeline / build-and-deploy (push) Failing after 24m19s
CD Pipeline / post-deploy-checks (push) Has been skipped
409 lines
14 KiB
Python
409 lines
14 KiB
Python
from __future__ import annotations
|
||
|
||
import os
|
||
from contextlib import asynccontextmanager
|
||
from datetime import UTC, datetime, timedelta
|
||
from uuid import UUID
|
||
|
||
import pytest
|
||
from fastapi import HTTPException
|
||
from pydantic import ValidationError
|
||
|
||
os.environ.setdefault(
|
||
"DATABASE_URL",
|
||
"postgresql+asyncpg://test:test@localhost/test",
|
||
)
|
||
|
||
from src.api.v1.platform import operator_runs # noqa: E402
|
||
from src.api.v1.platform import runs as public_runs # noqa: E402
|
||
from src.core.awooop_operator_auth import AwoooPOperatorPrincipal # noqa: E402
|
||
from src.db.awooop_models import AwoooPRunState # noqa: E402
|
||
from src.services import platform_operator_service, platform_runtime # noqa: E402
|
||
from src.services.paid_provider_canary_authorization import ( # noqa: E402
|
||
PAID_CANARY_AGENT_ID,
|
||
PAID_CANARY_PROJECT_ID,
|
||
PAID_CANARY_TRIGGER_REF,
|
||
PAID_CANARY_TRIGGER_TYPE,
|
||
paid_provider_canary_authorization_input_sha256,
|
||
)
|
||
from src.services.paid_provider_canary_gate5_run import ( # noqa: E402
|
||
PAID_CANARY_AUTHORIZATION_SCOPE,
|
||
PaidProviderCanaryGate5RunRecord,
|
||
create_paid_provider_canary_gate5_run,
|
||
paid_provider_canary_approval_contract_errors,
|
||
)
|
||
|
||
_SELECTED_RUN_ID = UUID("267a3d26-3c29-470f-8fe3-388a2f408f39")
|
||
|
||
|
||
class _Repository:
|
||
def __init__(self, *, duplicate: bool = False, state: str = "waiting_approval") -> None:
|
||
self.duplicate = duplicate
|
||
self.state = state
|
||
self.calls: list[dict[str, object]] = []
|
||
|
||
async def create_or_get(self, **kwargs: object) -> tuple[
|
||
PaidProviderCanaryGate5RunRecord,
|
||
bool,
|
||
]:
|
||
self.calls.append(kwargs)
|
||
run_id = kwargs["run_id"]
|
||
timeout_at = kwargs["timeout_at"]
|
||
input_sha256 = kwargs["input_sha256"]
|
||
trace_id = kwargs["trace_id"]
|
||
assert isinstance(run_id, UUID)
|
||
assert isinstance(timeout_at, datetime)
|
||
assert isinstance(input_sha256, str)
|
||
assert isinstance(trace_id, str)
|
||
if self.duplicate:
|
||
run_id = _SELECTED_RUN_ID
|
||
return (
|
||
PaidProviderCanaryGate5RunRecord(
|
||
run_id=run_id,
|
||
project_id=PAID_CANARY_PROJECT_ID,
|
||
agent_id=PAID_CANARY_AGENT_ID,
|
||
state=self.state,
|
||
trigger_type=PAID_CANARY_TRIGGER_TYPE,
|
||
trigger_ref=PAID_CANARY_TRIGGER_REF,
|
||
is_shadow=False,
|
||
input_sha256=input_sha256,
|
||
timeout_at=timeout_at,
|
||
trace_id=trace_id,
|
||
),
|
||
self.duplicate,
|
||
)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_operator_service_creates_exact_bounded_non_shadow_gate5_run() -> None:
|
||
now = datetime(2026, 7, 16, 9, 0, tzinfo=UTC)
|
||
repository = _Repository()
|
||
audit_calls: list[dict[str, object]] = []
|
||
|
||
async def audit_writer(**kwargs: object) -> None:
|
||
audit_calls.append(kwargs)
|
||
|
||
response = await create_paid_provider_canary_gate5_run(
|
||
operator_id="telegram:123456",
|
||
repository=repository,
|
||
audit_writer=audit_writer,
|
||
now=now,
|
||
)
|
||
|
||
assert response["project_id"] == "awoooi"
|
||
assert response["work_item_id"] == "AIA-SRE-013"
|
||
assert response["agent_id"] == "awoooi-paid-provider-canary"
|
||
assert response["state"] == "waiting_approval"
|
||
assert response["is_shadow"] is False
|
||
assert response["trigger_type"] == "api"
|
||
assert response["trigger_ref"] == "paid-provider-canary:AIA-SRE-013"
|
||
assert response["input_sha256"] == paid_provider_canary_authorization_input_sha256()
|
||
assert response["authorization_scope"] == PAID_CANARY_AUTHORIZATION_SCOPE
|
||
assert response["provider_order"] == [
|
||
"ollama_gcp_a",
|
||
"ollama_gcp_b",
|
||
"ollama_local",
|
||
"claude",
|
||
"gemini",
|
||
]
|
||
assert response["paid_provider_canary_percent"] == 5
|
||
assert response["canary_bucket"] < 5
|
||
assert response["authorization_run_is_execution_run"] is True
|
||
assert response["max_output_tokens_per_paid_provider"] == 512
|
||
assert response["max_total_paid_cost_usd"] == "0.25"
|
||
assert response["timeout_at"] == now + timedelta(minutes=15)
|
||
assert response["approval_ttl_seconds"] == 900
|
||
assert response["provider_call_performed"] is False
|
||
assert response["route_change_performed"] is False
|
||
assert response["infrastructure_write_performed"] is False
|
||
assert response["raw_prompt_persistence_allowed"] is False
|
||
assert response["raw_response_persistence_allowed"] is False
|
||
assert response["next_action"] == "operator_gate5_decision_required"
|
||
assert UUID(response["authorization_ref"]) == UUID(response["run_id"])
|
||
assert "telegram:123456" not in str(response)
|
||
|
||
assert len(repository.calls) == 1
|
||
call = repository.calls[0]
|
||
assert call["now"] == now
|
||
assert call["timeout_at"] == now + timedelta(minutes=15)
|
||
assert call["input_sha256"] == paid_provider_canary_authorization_input_sha256()
|
||
assert len(audit_calls) == 1
|
||
assert audit_calls[0]["resource_id"] == response["run_id"]
|
||
assert audit_calls[0]["run_id"] == response["run_id"]
|
||
details = audit_calls[0]["details"]
|
||
assert isinstance(details, dict)
|
||
assert details["provider_call_performed"] is False
|
||
assert details["route_change_performed"] is False
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_duplicate_active_authorization_is_reused_without_scope_expansion() -> None:
|
||
repository = _Repository(duplicate=True, state="running")
|
||
|
||
async def audit_writer(**_: object) -> None:
|
||
return None
|
||
|
||
response = await create_paid_provider_canary_gate5_run(
|
||
operator_id="ops@example.com",
|
||
repository=repository,
|
||
audit_writer=audit_writer,
|
||
now=datetime(2026, 7, 16, 9, 0, tzinfo=UTC),
|
||
)
|
||
|
||
assert response["is_duplicate"] is True
|
||
assert response["state"] == "running"
|
||
assert response["run_id"] == str(_SELECTED_RUN_ID)
|
||
assert response["max_total_paid_cost_usd"] == "0.25"
|
||
assert response["infrastructure_write_performed"] is False
|
||
assert response["next_action"] == (
|
||
"verify_authorization_then_run_bounded_canary"
|
||
)
|
||
|
||
|
||
def _run(
|
||
*,
|
||
now: datetime,
|
||
agent_id: str = PAID_CANARY_AGENT_ID,
|
||
trigger_ref: str = PAID_CANARY_TRIGGER_REF,
|
||
) -> AwoooPRunState:
|
||
return AwoooPRunState(
|
||
run_id=_SELECTED_RUN_ID,
|
||
project_id=PAID_CANARY_PROJECT_ID,
|
||
agent_id=agent_id,
|
||
state="waiting_approval",
|
||
trace_id="00-0123456789abcdef0123456789abcdef-0123456789abcdef-01",
|
||
trigger_type=PAID_CANARY_TRIGGER_TYPE,
|
||
trigger_ref=trigger_ref,
|
||
is_shadow=False,
|
||
input_sha256=paid_provider_canary_authorization_input_sha256(),
|
||
timeout_at=now + timedelta(minutes=15),
|
||
)
|
||
|
||
|
||
def test_paid_canary_approval_contract_is_exact_and_expiry_bound() -> None:
|
||
now = datetime(2026, 7, 16, 9, 0, tzinfo=UTC)
|
||
exact = _run(now=now)
|
||
expired = _run(now=now)
|
||
expired.timeout_at = now - timedelta(seconds=1)
|
||
shadow = _run(now=now)
|
||
shadow.is_shadow = True
|
||
drifted_hash = _run(now=now)
|
||
drifted_hash.input_sha256 = "0" * 64
|
||
|
||
assert paid_provider_canary_approval_contract_errors(exact, now=now) == []
|
||
assert paid_provider_canary_approval_contract_errors(expired, now=now) == [
|
||
"timeout_not_expired"
|
||
]
|
||
assert paid_provider_canary_approval_contract_errors(shadow, now=now) == [
|
||
"non_shadow"
|
||
]
|
||
assert paid_provider_canary_approval_contract_errors(
|
||
drifted_hash,
|
||
now=now,
|
||
) == ["input_sha256_exact"]
|
||
|
||
|
||
def test_unrelated_generic_run_does_not_enter_paid_canary_contract() -> None:
|
||
now = datetime(2026, 7, 16, 9, 0, tzinfo=UTC)
|
||
generic = _run(
|
||
now=now,
|
||
agent_id="generic-agent",
|
||
trigger_ref="generic:api:request",
|
||
)
|
||
|
||
assert paid_provider_canary_approval_contract_errors(generic, now=now) is None
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_generic_approval_service_rejects_expired_paid_canary_before_transition(
|
||
monkeypatch: pytest.MonkeyPatch,
|
||
) -> None:
|
||
now = datetime.now(UTC)
|
||
expired = _run(now=now)
|
||
expired.timeout_at = now - timedelta(seconds=1)
|
||
|
||
class _Result:
|
||
def scalar_one_or_none(self) -> AwoooPRunState:
|
||
return expired
|
||
|
||
class _Db:
|
||
async def execute(self, *_: object, **__: object) -> _Result:
|
||
return _Result()
|
||
|
||
@asynccontextmanager
|
||
async def fake_db_context(project_id: str):
|
||
assert project_id == "awoooi"
|
||
yield _Db()
|
||
|
||
monkeypatch.setattr(
|
||
platform_operator_service,
|
||
"get_db_context",
|
||
fake_db_context,
|
||
)
|
||
|
||
with pytest.raises(HTTPException) as exc:
|
||
await platform_operator_service.decide_approval(
|
||
run_id=str(expired.run_id),
|
||
project_id="awoooi",
|
||
decision="approve",
|
||
approver_id="telegram:123456",
|
||
reason="bounded canary",
|
||
)
|
||
|
||
assert exc.value.status_code == 409
|
||
assert "timeout_not_expired" in str(exc.value.detail)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_operator_route_uses_authenticated_principal_and_fixed_body(
|
||
monkeypatch: pytest.MonkeyPatch,
|
||
) -> None:
|
||
captured: dict[str, object] = {}
|
||
|
||
async def fake_create(**kwargs: object) -> dict[str, object]:
|
||
captured.update(kwargs)
|
||
return {
|
||
"schema_version": "paid_provider_canary_gate5_run_v1",
|
||
"authorization_ref": "018f2d04-4c37-7a18-b764-df0df0cbe111",
|
||
"run_id": "018f2d04-4c37-7a18-b764-df0df0cbe111",
|
||
"project_id": "awoooi",
|
||
"work_item_id": "AIA-SRE-013",
|
||
"agent_id": "awoooi-paid-provider-canary",
|
||
"state": "waiting_approval",
|
||
"is_shadow": False,
|
||
"is_duplicate": False,
|
||
"trigger_type": "api",
|
||
"trigger_ref": "paid-provider-canary:AIA-SRE-013",
|
||
"input_sha256": "1" * 64,
|
||
"authorization_scope": PAID_CANARY_AUTHORIZATION_SCOPE,
|
||
"provider_order": [
|
||
"ollama_gcp_a",
|
||
"ollama_gcp_b",
|
||
"ollama_local",
|
||
"claude",
|
||
"gemini",
|
||
],
|
||
"paid_provider_canary_percent": 5,
|
||
"max_output_tokens_per_paid_provider": 512,
|
||
"max_total_paid_cost_usd": "0.25",
|
||
"timeout_at": datetime(2026, 7, 16, 9, 15, tzinfo=UTC),
|
||
"approval_ttl_seconds": 900,
|
||
"provider_call_performed": False,
|
||
"route_change_performed": False,
|
||
"infrastructure_write_performed": False,
|
||
"raw_prompt_persistence_allowed": False,
|
||
"raw_response_persistence_allowed": False,
|
||
"next_action": "operator_gate5_decision_required",
|
||
"approval_path": (
|
||
"/api/v1/platform/approvals/"
|
||
"018f2d04-4c37-7a18-b764-df0df0cbe111/decide"
|
||
),
|
||
}
|
||
|
||
monkeypatch.setattr(
|
||
operator_runs,
|
||
"create_paid_provider_canary_gate5_run_svc",
|
||
fake_create,
|
||
)
|
||
body = operator_runs.CreatePaidProviderCanaryAuthorizationRequest(
|
||
work_item_id="AIA-SRE-013",
|
||
authorization_scope=PAID_CANARY_AUTHORIZATION_SCOPE,
|
||
)
|
||
principal = AwoooPOperatorPrincipal(
|
||
operator_id="telegram:123456",
|
||
auth_method="operator_api_key",
|
||
)
|
||
|
||
response = await operator_runs.create_paid_provider_canary_authorization(
|
||
body,
|
||
principal,
|
||
)
|
||
validated = (
|
||
operator_runs.CreatePaidProviderCanaryAuthorizationResponse.model_validate(
|
||
response
|
||
)
|
||
)
|
||
|
||
assert response["state"] == "waiting_approval"
|
||
assert validated.provider_order[-2:] == ("claude", "gemini")
|
||
assert captured == {"operator_id": "telegram:123456"}
|
||
|
||
with pytest.raises(ValidationError):
|
||
operator_runs.CreatePaidProviderCanaryAuthorizationRequest.model_validate(
|
||
{
|
||
"work_item_id": "OTHER",
|
||
"authorization_scope": PAID_CANARY_AUTHORIZATION_SCOPE,
|
||
}
|
||
)
|
||
|
||
|
||
def test_paid_canary_route_declares_operator_auth_dependency() -> None:
|
||
route = next(
|
||
route
|
||
for route in operator_runs.router.routes
|
||
if getattr(route, "path", "")
|
||
== "/approvals/paid-provider-canary/authorize"
|
||
)
|
||
|
||
assert any(
|
||
dependency.call is operator_runs.verify_awooop_operator
|
||
for dependency in route.dependant.dependencies
|
||
)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_generic_runtime_service_persists_shadow_true(
|
||
monkeypatch: pytest.MonkeyPatch,
|
||
) -> None:
|
||
captured: list[AwoooPRunState] = []
|
||
|
||
class _Db:
|
||
def add(self, run: AwoooPRunState) -> None:
|
||
captured.append(run)
|
||
|
||
@asynccontextmanager
|
||
async def fake_db_context(project_id: str):
|
||
assert project_id == "awoooi"
|
||
yield _Db()
|
||
|
||
monkeypatch.setattr(platform_runtime, "get_db_context", fake_db_context)
|
||
|
||
_, is_duplicate = await platform_runtime.create_run(
|
||
project_id="awoooi",
|
||
agent_id="generic-agent",
|
||
trigger_type="api",
|
||
input_payload={"safe": True},
|
||
)
|
||
|
||
assert is_duplicate is False
|
||
assert len(captured) == 1
|
||
assert captured[0].is_shadow is True
|
||
assert captured[0].state == "pending"
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_generic_public_run_creation_remains_shadow_only(
|
||
monkeypatch: pytest.MonkeyPatch,
|
||
) -> None:
|
||
async def fake_create_run(**_: object) -> tuple[UUID, bool]:
|
||
return UUID("018f2d04-4c37-7a18-b764-df0df0cbe222"), False
|
||
|
||
async def fake_write_audit(**kwargs: object) -> None:
|
||
details = kwargs["details"]
|
||
assert isinstance(details, dict)
|
||
assert details["is_shadow"] is True
|
||
|
||
monkeypatch.setattr(public_runs, "create_run", fake_create_run)
|
||
monkeypatch.setattr(public_runs, "write_audit", fake_write_audit)
|
||
|
||
response = await public_runs.create_platform_run(
|
||
public_runs.CreateRunRequest(
|
||
project_id="awoooi",
|
||
agent_id="generic-agent",
|
||
trigger_type="api",
|
||
)
|
||
)
|
||
|
||
assert response.is_shadow is True
|
||
assert response.message == "Run 已接受(shadow mode)"
|