semantics

This commit is contained in:
2026-03-27 21:27:31 +03:00
parent 7c85552132
commit 2ed66bfebc
182 changed files with 21186 additions and 10254 deletions

View File

@@ -3,7 +3,7 @@
# @COMPLEXITY: 3
# @PURPOSE: Unit tests for MigrationArchiveParser ZIP extraction contract.
# @LAYER: Domain
# @RELATION: VERIFIES -> backend.src.core.migration.archive_parser
# @RELATION: DEPENDS_ON -> [MigrationArchiveParserModule]
#
import os
import sys
@@ -23,6 +23,11 @@ from src.core.migration.archive_parser import MigrationArchiveParser
# [DEF:test_extract_objects_from_zip_collects_all_types:Function]
# @RELATION: BINDS_TO -> TestArchiveParser
# @PURPOSE: Verify archive parser collects dashboard/chart/dataset YAML objects into typed buckets.
# @TEST_CONTRACT: zip_archive_fixture -> typed dashboard/chart/dataset extraction buckets
# @TEST_SCENARIO: archive_with_supported_objects_extracts_all_types -> One YAML file per supported type lands in matching bucket.
# @TEST_EDGE: missing_field -> Missing typed folder prevents that object class from being extracted.
# @TEST_EDGE: invalid_type -> Unsupported file layout is ignored by typed extraction buckets.
# @TEST_EDGE: external_fail -> Corrupt archive would fail extraction before typed buckets are returned.
def test_extract_objects_from_zip_collects_all_types():
parser = MigrationArchiveParser()
with tempfile.TemporaryDirectory() as td:

View File

@@ -3,7 +3,7 @@
# @COMPLEXITY: 3
# @PURPOSE: Unit tests for MigrationDryRunService diff and risk computation contracts.
# @LAYER: Domain
# @RELATION: VERIFIES -> [MigrationDryRunOrchestratorModule]
# @RELATION: DEPENDS_ON -> [MigrationDryRunOrchestratorModule]
#
import json
import sys
@@ -56,6 +56,11 @@ def _make_session():
# [DEF:test_migration_dry_run_service_builds_diff_and_risk:Function]
# @RELATION: BINDS_TO -> [TestDryRunOrchestrator]
# @PURPOSE: Verify dry-run orchestration returns stable diff summary and required risk codes.
# @TEST_SCENARIO: dry_run_builds_diff_and_risk -> Stable diff summary and required risk codes are returned.
# @TEST_EDGE: missing_field -> Missing target datasource remains visible in risk items.
# @TEST_EDGE: invalid_type -> Broken dataset reference remains visible in risk items.
# @TEST_EDGE: external_fail -> Engine transform stub failure would stop result production.
# @TEST_INVARIANT: dry_run_result_contract_matches_fixture -> VERIFIED_BY: [dry_run_builds_diff_and_risk]
def test_migration_dry_run_service_builds_diff_and_risk():
# @TEST_CONTRACT: dry_run_result_contract -> {
# required_fields: {diff: object, summary: object, risk: object},

View File

@@ -1,5 +1,6 @@
# [DEF:TestResourceHubs:Module]
# @RELATION: BELONGS_TO -> SrcRoot
# @RELATION: DEPENDS_ON -> [DashboardsApi]
# @RELATION: DEPENDS_ON -> [DatasetsApi]
# @COMPLEXITY: 3
# @SEMANTICS: tests, resource-hubs, dashboards, datasets, pagination, api
# @PURPOSE: Contract tests for resource hub dashboards/datasets listing and pagination boundary validation.
@@ -17,14 +18,20 @@ from src.dependencies import (
client = TestClient(app)
# [DEF:test_dashboards_api:Test]
# @RELATION: BINDS_TO -> TestResourceHubs
# [DEF:test_dashboards_api:Block]
# @RELATION: BINDS_TO -> [TestResourceHubs]
# @PURPOSE: Verify GET /api/dashboards contract compliance
# @TEST: Valid env_id returns 200 and dashboard list
# @TEST: Invalid env_id returns 404
# @TEST: Search filter works
# @TEST_CONTRACT: dashboards_query -> dashboards payload or not_found response
# @TEST_SCENARIO: dashboards_env_found_returns_payload -> HTTP 200 returns normalized dashboards list.
# @TEST_SCENARIO: dashboards_unknown_env_returns_not_found -> HTTP 404 is returned for unknown env_id.
# @TEST_SCENARIO: dashboards_search_filters_results -> Search narrows payload to matching dashboard title.
# @TEST_INVARIANT: dashboards_route_contract_stays_observable -> VERIFIED_BY: [dashboards_env_found_returns_payload, dashboards_unknown_env_returns_not_found, dashboards_search_filters_results]
# [DEF:mock_deps:Function]
# @PURPOSE: Provide dependency override fixture for resource hub route tests.
# @RELATION: BINDS_TO -> [TestResourceHubs]
# @TEST_FIXTURE: resource_hub_overrides -> INLINE_JSON
@pytest.fixture
def mock_deps():
# @INVARIANT: unconstrained mock — no spec= enforced; attribute typos will silently pass
@@ -89,8 +96,11 @@ def mock_deps():
app.dependency_overrides.clear()
# [/DEF:mock_deps:Function]
# [DEF:test_get_dashboards_success:Function]
# @RELATION: BINDS_TO -> test_dashboards_api
# @RELATION: BINDS_TO -> [test_dashboards_api]
# @PURPOSE: Verify dashboards endpoint returns 200 with expected dashboard payload fields.
def test_get_dashboards_success(mock_deps):
response = client.get("/api/dashboards?env_id=env1")
@@ -106,7 +116,7 @@ def test_get_dashboards_success(mock_deps):
# [DEF:test_get_dashboards_not_found:Function]
# @RELATION: BINDS_TO -> test_dashboards_api
# @RELATION: BINDS_TO -> [test_dashboards_api]
# @PURPOSE: Verify dashboards endpoint returns 404 for unknown environment identifier.
def test_get_dashboards_not_found(mock_deps):
response = client.get("/api/dashboards?env_id=invalid")
@@ -117,7 +127,7 @@ def test_get_dashboards_not_found(mock_deps):
# [DEF:test_get_dashboards_search:Function]
# @RELATION: BINDS_TO -> test_dashboards_api
# @RELATION: BINDS_TO -> [test_dashboards_api]
# @PURPOSE: Verify dashboards endpoint search filter returns matching subset.
def test_get_dashboards_search(mock_deps):
response = client.get("/api/dashboards?env_id=env1&search=Sales")
@@ -128,20 +138,22 @@ def test_get_dashboards_search(mock_deps):
# [/DEF:test_get_dashboards_search:Function]
# [/DEF:test_dashboards_api:Test]
# [/DEF:test_dashboards_api:Block]
# [DEF:test_datasets_api:Test]
# @RELATION: BINDS_TO -> TestResourceHubs
# [DEF:test_datasets_api:Block]
# @RELATION: BINDS_TO -> [TestResourceHubs]
# @PURPOSE: Verify GET /api/datasets contract compliance
# @TEST: Valid env_id returns 200 and dataset list
# @TEST: Invalid env_id returns 404
# @TEST: Search filter works
# @TEST: Negative - Service failure returns 503
# @TEST_CONTRACT: datasets_query -> datasets payload or error response
# @TEST_SCENARIO: datasets_env_found_returns_payload -> HTTP 200 returns normalized datasets list.
# @TEST_SCENARIO: datasets_unknown_env_returns_not_found -> HTTP 404 is returned for unknown env_id.
# @TEST_SCENARIO: datasets_search_filters_results -> Search narrows payload to matching dataset table.
# @TEST_SCENARIO: datasets_service_failure_returns_503 -> Backend fetch failure surfaces as HTTP 503.
# @TEST_INVARIANT: datasets_route_contract_stays_observable -> VERIFIED_BY: [datasets_env_found_returns_payload, datasets_unknown_env_returns_not_found, datasets_search_filters_results, datasets_service_failure_returns_503]
# [DEF:test_get_datasets_success:Function]
# @RELATION: BINDS_TO -> test_datasets_api
# @RELATION: BINDS_TO -> [test_datasets_api]
# @PURPOSE: Verify datasets endpoint returns 200 with mapped fields payload.
def test_get_datasets_success(mock_deps):
mock_deps["resource"].get_datasets_with_status = AsyncMock(
@@ -170,7 +182,7 @@ def test_get_datasets_success(mock_deps):
# [DEF:test_get_datasets_not_found:Function]
# @RELATION: BINDS_TO -> test_datasets_api
# @RELATION: BINDS_TO -> [test_datasets_api]
# @PURPOSE: Verify datasets endpoint returns 404 for unknown environment identifier.
def test_get_datasets_not_found(mock_deps):
response = client.get("/api/datasets?env_id=invalid")
@@ -181,7 +193,7 @@ def test_get_datasets_not_found(mock_deps):
# [DEF:test_get_datasets_search:Function]
# @RELATION: BINDS_TO -> test_datasets_api
# @RELATION: BINDS_TO -> [test_datasets_api]
# @PURPOSE: Verify datasets endpoint search filter returns matching dataset subset.
def test_get_datasets_search(mock_deps):
mock_deps["resource"].get_datasets_with_status = AsyncMock(
@@ -216,7 +228,7 @@ def test_get_datasets_search(mock_deps):
# [DEF:test_get_datasets_service_failure:Function]
# @RELATION: BINDS_TO -> test_datasets_api
# @RELATION: BINDS_TO -> [test_datasets_api]
# @PURPOSE: Verify datasets endpoint surfaces backend fetch failure as HTTP 503.
def test_get_datasets_service_failure(mock_deps):
mock_deps["resource"].get_datasets_with_status = AsyncMock(
@@ -229,20 +241,28 @@ def test_get_datasets_service_failure(mock_deps):
# [/DEF:test_get_datasets_service_failure:Function]
# [/DEF:test_datasets_api:Test]
# [/DEF:test_datasets_api:Block]
# [DEF:test_pagination_boundaries:Test]
# @RELATION: BINDS_TO -> TestResourceHubs
# [DEF:test_pagination_boundaries:Block]
# @RELATION: BINDS_TO -> [TestResourceHubs]
# @PURPOSE: Verify pagination validation for GET endpoints
# @TEST: page<1 and page_size>100 return 400
# @TEST_CONTRACT: pagination_query -> validation error response
# @TEST_SCENARIO: dashboards_zero_page_rejected -> page=0 returns HTTP 400.
# @TEST_SCENARIO: dashboards_oversize_page_rejected -> page_size=101 returns HTTP 400.
# @TEST_SCENARIO: datasets_zero_page_rejected -> page=0 returns HTTP 400.
# @TEST_SCENARIO: datasets_oversize_page_rejected -> page_size=101 returns HTTP 400.
# @TEST_EDGE: missing_field -> Missing env_id prevents route contract completion.
# @TEST_EDGE: invalid_type -> Invalid pagination values are rejected at route validation layer.
# @TEST_EDGE: external_fail -> Validation failure returns HTTP 400 without partial payload.
# @TEST_INVARIANT: pagination_limits_apply_to_both_routes -> VERIFIED_BY: [dashboards_zero_page_rejected, dashboards_oversize_page_rejected, datasets_zero_page_rejected, datasets_oversize_page_rejected]
# [DEF:test_get_dashboards_pagination_zero_page:Function]
# @RELATION: BINDS_TO -> test_pagination_boundaries
# @RELATION: BINDS_TO -> [test_pagination_boundaries]
# @PURPOSE: Verify dashboards endpoint rejects page=0 with HTTP 400 validation error.
def test_get_dashboards_pagination_zero_page(mock_deps):
"""@TEST_EDGE: pagination_zero_page -> {page:0, status:400}"""
# @TEST_EDGE: pagination_zero_page -> {page: 0, status: 400}
response = client.get("/api/dashboards?env_id=env1&page=0")
assert response.status_code == 400
assert "Page must be >= 1" in response.json()["detail"]
@@ -252,10 +272,10 @@ def test_get_dashboards_pagination_zero_page(mock_deps):
# [DEF:test_get_dashboards_pagination_oversize:Function]
# @RELATION: BINDS_TO -> test_pagination_boundaries
# @RELATION: BINDS_TO -> [test_pagination_boundaries]
# @PURPOSE: Verify dashboards endpoint rejects oversized page_size with HTTP 400.
def test_get_dashboards_pagination_oversize(mock_deps):
"""@TEST_EDGE: pagination_oversize -> {page_size:101, status:400}"""
# @TEST_EDGE: pagination_oversize -> {page_size: 101, status: 400}
response = client.get("/api/dashboards?env_id=env1&page_size=101")
assert response.status_code == 400
assert "Page size must be between 1 and 100" in response.json()["detail"]
@@ -265,10 +285,10 @@ def test_get_dashboards_pagination_oversize(mock_deps):
# [DEF:test_get_datasets_pagination_zero_page:Function]
# @RELATION: BINDS_TO -> test_pagination_boundaries
# @RELATION: BINDS_TO -> [test_pagination_boundaries]
# @PURPOSE: Verify datasets endpoint rejects page=0 with HTTP 400.
def test_get_datasets_pagination_zero_page(mock_deps):
"""@TEST_EDGE: pagination_zero_page on datasets"""
# @TEST_EDGE: pagination_zero_page_datasets -> {page: 0, status: 400}
response = client.get("/api/datasets?env_id=env1&page=0")
assert response.status_code == 400
@@ -277,14 +297,14 @@ def test_get_datasets_pagination_zero_page(mock_deps):
# [DEF:test_get_datasets_pagination_oversize:Function]
# @RELATION: BINDS_TO -> test_pagination_boundaries
# @RELATION: BINDS_TO -> [test_pagination_boundaries]
# @PURPOSE: Verify datasets endpoint rejects oversized page_size with HTTP 400.
def test_get_datasets_pagination_oversize(mock_deps):
"""@TEST_EDGE: pagination_oversize on datasets"""
# @TEST_EDGE: pagination_oversize_datasets -> {page_size: 101, status: 400}
response = client.get("/api/datasets?env_id=env1&page_size=101")
assert response.status_code == 400
# [/DEF:test_get_datasets_pagination_oversize:Function]
# [/DEF:test_pagination_boundaries:Test]
# [/DEF:test_pagination_boundaries:Block]
# [/DEF:TestResourceHubs:Module]