Files
ss-tools/backend/tests/api/test_dashboard_testing_inheritance.py
busya a32ca0631b feat(037): capture, verification lifecycle, inheritance + close 036 stabilization
- Authoritative candidate capture with server-issued artifacts and raw-byte
  immutability hashing (source_response_hash server-owned)
- Closed-period lifecycle: request-hash bound approvals, persisted closure
  immutability violations, byte-for-byte catalog stability on reclosure
- Verification runs: persisted VerificationRun model + FK migration,
  publish gate (block_publish), scheduled observability runs (02:00 UTC)
- FR-013 baseline inheritance: prior_release_id migration, plan_inheritance/
  execute_inheritance classification and re-extraction, API endpoints
- Visual executor bound to release-deployment environment; caller mismatch
  rejected; visual SSIM/reconciliation modules
- Query execution decomposed: envelope/model/executor split, no direct SQL
- AgentRun approvals extracted to submodule; evidence adapter; _utils
- Dashboard testing service decomposed into 30+ modules (all <400 LOC)
- Five Feature-037 agent tools with permission guards (tools_037.py)
- API readiness endpoint; Alembic env/migrations; test fixture repos
- Specs 036/037 contracts, openapi.yaml, schema.json, tasks/traceability
  updated; semantic index rebuilt with 0 parse warnings
- Fix ADR-0003 parser ambiguity: remove [DEF🆔ADR] prose example
- Add axiom-mcp-agent-feedback.md: agent findings for MCP rework plan
- Tests: 298 service + 1464 API + 45 agent passing; ruff clean
2026-07-31 11:28:50 +03:00

172 lines
7.8 KiB
Python

# #region Test.Api.DashboardTesting.Inheritance [C:4] [TYPE Module] [SEMANTICS testing,api,dashboard-testing,inheritance]
# @defgroup API contract tests for baseline inheritance endpoints — plan and execute.
# @LAYER Test
# @RELATION VERIFIES -> [Api.DashboardTesting.Inheritance]
# @TEST_INVARIANT POST /inheritance/plan returns plan with correct counts.
# @TEST_INVARIANT POST /inheritance/plan rejects same release IDs.
# @TEST_INVARIANT POST /inheritance/execute requires valid plan_id.
# @TEST_INVARIANT POST /inheritance/plan rejects missing releases.
from __future__ import annotations
import pytest
from unittest.mock import MagicMock, patch
from uuid import uuid4
from fastapi.testclient import TestClient
from src.app import app
# #region Test.Api.DashboardTesting.Inheritance.Fixtures [C:1] [TYPE Class] [SEMANTICS testing,api,dashboard-testing,inheritance,fixtures]
@pytest.fixture
def inheritance_client() -> TestClient:
"""Provide TestClient without auth dependency overrides."""
from src.dependencies import get_current_user
from src.models.auth import Role, User
admin_role = Role(id="admin-role-test-001", name="Admin", is_admin=True)
user = User(id="test-user-inheritance", username="tester", email="tester@test.com")
user.roles = [admin_role]
app.dependency_overrides[get_current_user] = lambda: user
try:
yield TestClient(app)
finally:
app.dependency_overrides.pop(get_current_user, None)
# #endregion Test.Api.DashboardTesting.Inheritance.Fixtures
# #region Test.Api.DashboardTesting.Inheritance.RouterRegistration [C:2] [TYPE Function] [SEMANTICS testing,api,dashboard-testing,inheritance,registration]
# @BRIEF The inheritance router is registered and accessible.
def test_inheritance_routes_registered():
"""Inheritance plan and execute routes are registered in the router."""
from src.api.routes.dashboard_testing import router
paths = [
route.path for route in router.routes
if hasattr(route, "path") and "inheritance" in route.path
]
assert "/api/dashboard-testing/inheritance/plan" in paths
assert "/api/dashboard-testing/inheritance/execute" in paths
# #endregion Test.Api.DashboardTesting.Inheritance.RouterRegistration
# #region Test.Api.DashboardTesting.Inheritance.PlanEndpoint [C:3] [TYPE Class] [SEMANTICS testing,api,dashboard-testing,inheritance,plan]
class TestInheritancePlanEndpoint:
"""Tests for POST /api/dashboard-testing/inheritance/plan."""
# #region Test.Api.DashboardTesting.Inheritance.PlanEndpoint.SameReleaseIds [C:2] [TYPE Function]
# @TEST_EDGE: Same prior and current release ID returns 422
def test_same_release_ids_rejected(self, inheritance_client: TestClient):
"""plan endpoint rejects request where prior_release_id == current_release_id."""
release_id = str(uuid4())
response = inheritance_client.post(
"/api/dashboard-testing/inheritance/plan",
json={
"prior_release_id": release_id,
"current_release_id": release_id,
},
)
assert response.status_code == 422, f"Expected 422, got {response.status_code}: {response.text}"
assert "must differ" in response.text
# #endregion Test.Api.DashboardTesting.Inheritance.PlanEndpoint.SameReleaseIds
# #region Test.Api.DashboardTesting.Inheritance.PlanEndpoint.MissingReleases [C:2] [TYPE Function]
# @TEST_EDGE: Missing releases return 404
def test_missing_prior_release_returns_404(self, inheritance_client: TestClient):
"""plan endpoint returns 404 when prior release does not exist."""
response = inheritance_client.post(
"/api/dashboard-testing/inheritance/plan",
json={
"prior_release_id": "nonexistent-prior",
"current_release_id": "nonexistent-current",
},
)
assert response.status_code == 404, f"Expected 404, got {response.status_code}: {response.text}"
# #endregion Test.Api.DashboardTesting.Inheritance.PlanEndpoint.MissingReleases
# #region Test.Api.DashboardTesting.Inheritance.PlanEndpoint.Success [C:2] [TYPE Function]
# @TEST_EDGE: Successful plan returns InheritancePlanResponse with counts
@patch("src.api.routes.dashboard_testing.inheritance.plan_inheritance")
@patch("src.api.routes.dashboard_testing.inheritance.build_plan_response")
def test_successful_plan(
self,
mock_build_response: MagicMock,
mock_plan: MagicMock,
inheritance_client: TestClient,
):
"""Successful plan endpoint returns InheritancePlanResponse."""
from src.schemas.dashboard_testing.inheritance import InheritancePlanResponse
mock_plan.return_value = MagicMock()
mock_build_response.return_value = InheritancePlanResponse(
plan_id="test-plan-1",
prior_release_id="prior-uuid",
current_release_id="current-uuid",
inherited_count=3,
changed_count=1,
new_count=0,
entries=[],
)
response = inheritance_client.post(
"/api/dashboard-testing/inheritance/plan",
json={
"prior_release_id": "prior-uuid",
"current_release_id": "current-uuid",
},
)
assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}"
data = response.json()
assert data["plan_id"] == "test-plan-1"
assert data["inherited_count"] == 3
assert data["changed_count"] == 1
assert data["new_count"] == 0
# #endregion Test.Api.DashboardTesting.Inheritance.PlanEndpoint.Success
# #endregion Test.Api.DashboardTesting.Inheritance.PlanEndpoint
# #region Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint [C:3] [TYPE Class] [SEMANTICS testing,api,dashboard-testing,inheritance,execute]
class TestInheritanceExecuteEndpoint:
"""Tests for POST /api/dashboard-testing/inheritance/execute."""
# #region Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint.InvalidPlanId [C:2] [TYPE Function]
# @TEST_EDGE: Invalid plan_id returns 400
def test_invalid_plan_id_returns_400(self, inheritance_client: TestClient):
"""execute endpoint returns 400 for invalid plan_id."""
response = inheritance_client.post(
"/api/dashboard-testing/inheritance/execute",
json={
"plan_id": "invalid-plan",
"target_environment_id": "ss-preprod",
},
)
assert response.status_code == 400, f"Expected 400, got {response.status_code}: {response.text}"
# #endregion Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint.InvalidPlanId
# #region Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint.MissingTargetEnv [C:2] [TYPE Function]
# @TEST_EDGE: Missing target environment returns 404
@patch("src.api.routes.dashboard_testing.inheritance.get_config_manager")
def test_missing_target_env_returns_404(
self,
mock_config: MagicMock,
inheritance_client: TestClient,
):
"""execute endpoint returns 404 for unknown target environment."""
mock_mgr = MagicMock()
mock_mgr.get_environment.return_value = None
mock_config.return_value = mock_mgr
response = inheritance_client.post(
"/api/dashboard-testing/inheritance/execute",
json={
"plan_id": "prior-uuid:current-uuid",
"target_environment_id": "nonexistent-env",
},
)
assert response.status_code == 404, f"Expected 404, got {response.status_code}: {response.text}"
# #endregion Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint.MissingTargetEnv
# #endregion Test.Api.DashboardTesting.Inheritance.ExecuteEndpoint
# #endregion Test.Api.DashboardTesting.Inheritance