test(036): backend schemas (22 tests) + frontend AgentRunModel (13 tests)

This commit is contained in:
2026-07-28 10:35:47 +03:00
parent eb0bd201a8
commit 24fca2ec8a
4 changed files with 367 additions and 0 deletions

View File

@@ -0,0 +1,204 @@
# backend/tests/services/agent_runs/test_schemas.py
# #region Test.AgentRuns.Schemas [C:3] [TYPE Module] [SEMANTICS test,agent-run,schema,validation]
# @BRIEF L1 unit tests for AgentRun Pydantic schemas — no DB dependency.
# @RELATION BINDS_TO -> [Schemas.AgentRun]
# @TEST_EDGE invalid_v2_context -> 422
# @TEST_EDGE scenario_intent_with_v1 -> invalid
# @TEST_EDGE unknown_intent -> invalid
import pytest
from pydantic import ValidationError as PydanticValidationError
from src.schemas.agent_run import (
AgentRunSnapshot,
AppendEventRequest,
ApprovalDecisionRequest,
ApprovalRequest,
CreateAgentRunRequest,
DraftArtifactRef,
EventStatus,
RegisterDraftRequest,
StageEnum,
UIContextV2,
ValidationStatus,
)
class TestUIContextV2:
def test_valid_v1_context_passes(self):
ctx = UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=1, intent=None,
)
assert ctx.objectId == "42"
assert ctx.contextVersion == 1
def test_valid_v2_scenario_context_passes(self):
ctx = UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=2,
intent="build_dashboard_test_scenario",
)
assert ctx.intent == "build_dashboard_test_scenario"
def test_scenario_intent_with_v1_is_invalid(self):
with pytest.raises(PydanticValidationError):
UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=1,
intent="build_dashboard_test_scenario",
)
def test_v2_with_non_dashboard_rejected(self):
with pytest.raises(PydanticValidationError):
UIContextV2(
objectType="dataset", objectId="1", envId="dev",
route="/datasets/1", contextVersion=2,
intent="build_dashboard_test_scenario",
)
def test_unknown_intent_rejected(self):
with pytest.raises(PydanticValidationError):
UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=2,
intent="run_sql",
)
def test_objectId_must_be_numeric(self):
with pytest.raises(PydanticValidationError):
UIContextV2(
objectType="dashboard", objectId="abc", envId="dev",
route="/dashboards/42", contextVersion=1,
)
def test_route_must_start_with_dashboards_for_v2(self):
ctx = UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=2,
intent="build_dashboard_test_scenario",
)
assert ctx.route.startswith("/dashboards/")
class TestCreateAgentRunRequest:
def test_valid_request(self):
ctx = UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=2,
intent="build_dashboard_test_scenario",
)
req = CreateAgentRunRequest(context=ctx)
assert req.context.intent == "build_dashboard_test_scenario"
def test_with_conversation_id(self):
ctx = UIContextV2(
objectType="dashboard", objectId="42", envId="dev",
route="/dashboards/42", contextVersion=2,
intent="build_dashboard_test_scenario",
)
req = CreateAgentRunRequest(context=ctx, conversation_id="conv-123")
assert req.conversation_id == "conv-123"
class TestAppendEventRequest:
def test_valid_progress_event(self):
req = AppendEventRequest(
event_type="progress", stage=StageEnum.inspect,
status=EventStatus.active, sequence=1,
)
assert req.event_type == "progress"
assert req.stage == StageEnum.inspect
def test_sequence_must_be_positive(self):
with pytest.raises(PydanticValidationError):
AppendEventRequest(event_type="progress", sequence=0)
def test_event_type_required(self):
with pytest.raises(PydanticValidationError):
AppendEventRequest(sequence=1)
class TestApprovalRequest:
def test_valid_repository_write_gate(self):
req = ApprovalRequest(
operation="repository_write",
request_hash="a" * 64,
target_paths=["dashboard-tests/FI-0080/scenario.yaml"],
required_permission="dashboard:testing:WRITE",
)
assert req.operation == "repository_write"
assert len(req.target_paths) == 1
def test_reason_required_for_baseline_approval(self):
req = ApprovalRequest(
operation="baseline_approval",
request_hash="b" * 64,
target_paths=["baselines.yaml"],
required_permission="dashboard:testing:APPROVE",
reason_required=True,
)
assert req.reason_required is True
def test_empty_target_paths_rejected(self):
with pytest.raises(PydanticValidationError):
ApprovalRequest(
operation="repository_write",
request_hash="c" * 64,
target_paths=[],
required_permission="dashboard:testing:WRITE",
)
class TestApprovalDecisionRequest:
def test_confirm_with_reason(self):
req = ApprovalDecisionRequest(decision="confirm", reason="Initial QA baseline")
assert req.decision == "confirm"
def test_deny_without_reason(self):
req = ApprovalDecisionRequest(decision="deny")
assert req.decision == "deny"
def test_invalid_decision_rejected(self):
with pytest.raises(PydanticValidationError):
ApprovalDecisionRequest(decision="maybe")
class TestAgentRunSnapshot:
def test_minimal_snapshot(self):
snap = AgentRunSnapshot(
id="run-1", user_id="user-1", intent="dashboard_scenario_build",
trigger="manual", dashboard_id="42", environment_id="dev",
context_snapshot={}, status="CREATED", last_sequence=0,
created_at="2026-01-01T00:00:00Z", updated_at="2026-01-01T00:00:00Z",
)
assert snap.id == "run-1"
assert snap.stages == []
assert snap.drafts == []
class TestDraftArtifactRef:
def test_draft_ref(self):
ref = DraftArtifactRef(
id="draft-1", kind="scenario", name="test.yaml",
intended_path="test.yaml", sha256="a" * 64,
validation_status="pending",
)
assert ref.kind == "scenario"
class TestRegisterDraftRequest:
def test_valid_draft(self):
req = RegisterDraftRequest(
kind="scenario", name="scenario.yaml",
intended_path="dashboard-tests/scenario.yaml",
sha256="a" * 64,
)
assert req.intended_path == "dashboard-tests/scenario.yaml"
def test_invalid_sha256_length_rejected(self):
with pytest.raises(PydanticValidationError):
RegisterDraftRequest(
kind="scenario", name="scenario.yaml",
intended_path="test.yaml", sha256="too_short",
)
# #endregion Test.AgentRuns.Schemas