7e6f6fae37
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 12s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 17s
CI / typecheck (pull_request) Successful in 31s
CI / security (pull_request) Successful in 31s
CI / unit_tests (pull_request) Successful in 1m46s
CI / docker (pull_request) Successful in 38s
CI / integration_tests (pull_request) Successful in 2m52s
CI / coverage (pull_request) Successful in 3m41s
CI / benchmark-regression (pull_request) Successful in 23m24s
CI / lint (push) Successful in 14s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 17s
CI / typecheck (push) Successful in 33s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 35s
CI / unit_tests (push) Successful in 2m13s
CI / docker (push) Successful in 38s
CI / integration_tests (push) Successful in 2m58s
CI / coverage (push) Successful in 3m44s
CI / benchmark-publish (push) Successful in 13m19s
Add comprehensive semantic validation test suites covering five
new fixture categories: language porting mismatches, dependency
graph violations, API surface changes, cross-file symbols, and
circular import detection.
New BDD scenarios (38) exercise all six built-in rules against
fixture-driven inputs. Robot Framework integration tests (11)
validate end-to-end rule execution via the SemanticValidationService.
ASV benchmarks (6 suites) establish performance baselines for
batch validation throughput and per-rule latency.
Files added:
- features/fixtures/validation/{language_porting_mismatches,
dependency_graph_violations, api_surface_changes,
cross_file_symbols, circular_import_detection}.json
- features/semantic_validation_suite.feature
- features/steps/semantic_validation_suite_steps.py
- robot/semantic_validation_suite.robot
- robot/helper_semantic_validation_suite.py
- benchmarks/semantic_validation_suite_bench.py
- docs/reference/semantic_validation_coverage.md
All 11 nox sessions pass (lint, format, typecheck, security_scan,
dead_code, unit_tests, integration_tests, docs, build, benchmark,
coverage_report). Coverage remains at 97%.
ISSUES CLOSED: #316
174 lines
5.7 KiB
Python
174 lines
5.7 KiB
Python
"""Step definitions for semantic validation suite scenarios.
|
|
|
|
Loads JSON fixture files covering language-porting mismatches, dependency
|
|
graph violations, API surface changes, cross-file symbol resolution,
|
|
and circular import detection. Runs the appropriate semantic validation
|
|
rule against each fixture and asserts the expected outcome.
|
|
|
|
Uses :mod:`features.fixtures.validation.suite_helpers` for shared
|
|
loading, lookup, and verification logic.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from behave import given, then, when
|
|
|
|
if TYPE_CHECKING:
|
|
from behave.runner import Context
|
|
|
|
# Ensure the fixture helpers package is importable
|
|
_FIXTURES_PARENT = str(Path(__file__).resolve().parents[1] / "fixtures" / "validation")
|
|
if _FIXTURES_PARENT not in sys.path:
|
|
sys.path.insert(0, _FIXTURES_PARENT)
|
|
|
|
from suite_helpers import ( # noqa: E402
|
|
API_FILENAME,
|
|
CIRCULAR_FILENAME,
|
|
CROSS_FILE_FILENAME,
|
|
DEPENDENCY_FILENAME,
|
|
PORTING_FILENAME,
|
|
find_fixture,
|
|
load_fixtures,
|
|
validate_fixture_schema,
|
|
verify_fixture_result,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lazy fixture cache (FLAW-3 fix: avoid module-level loading)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_fixture_cache: dict[str, list[dict[str, Any]]] = {}
|
|
|
|
|
|
def _get_fixtures(filename: str) -> list[dict[str, Any]]:
|
|
"""Lazily load and cache fixtures by filename."""
|
|
if filename not in _fixture_cache:
|
|
_fixture_cache[filename] = load_fixtures(filename)
|
|
return _fixture_cache[filename]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Background
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a semantic validation suite test environment")
|
|
def step_suite_env(context: Context) -> None:
|
|
context.suite_fixture = None
|
|
context.suite_result = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Given steps - load individual fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('the porting fixture "{name}"')
|
|
def step_porting_fixture(context: Context, name: str) -> None:
|
|
context.suite_fixture = find_fixture(_get_fixtures(PORTING_FILENAME), name)
|
|
|
|
|
|
@given('the dependency fixture "{name}"')
|
|
def step_dependency_fixture(context: Context, name: str) -> None:
|
|
context.suite_fixture = find_fixture(_get_fixtures(DEPENDENCY_FILENAME), name)
|
|
|
|
|
|
@given('the API surface fixture "{name}"')
|
|
def step_api_fixture(context: Context, name: str) -> None:
|
|
context.suite_fixture = find_fixture(_get_fixtures(API_FILENAME), name)
|
|
|
|
|
|
@given('the cross-file fixture "{name}"')
|
|
def step_cross_file_fixture(context: Context, name: str) -> None:
|
|
context.suite_fixture = find_fixture(_get_fixtures(CROSS_FILE_FILENAME), name)
|
|
|
|
|
|
@given('the circular import fixture "{name}"')
|
|
def step_circular_fixture(context: Context, name: str) -> None:
|
|
context.suite_fixture = find_fixture(_get_fixtures(CIRCULAR_FILENAME), name)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When step - run the expected rule
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("the suite runs the expected rule")
|
|
def step_run_expected_rule(context: Context) -> None:
|
|
from suite_helpers import RULE_MAP
|
|
|
|
fixture = context.suite_fixture
|
|
if fixture is None:
|
|
msg = "No fixture loaded"
|
|
raise RuntimeError(msg)
|
|
rule_name: str = fixture["expected_rule"]
|
|
rule = RULE_MAP[rule_name]
|
|
source: str = fixture["source"]
|
|
filename: str = fixture["filename"]
|
|
context.suite_result = rule.check(source, filename)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Then steps - verify outcomes (with data field verification: FLAW-2 fix)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("the suite result matches the fixture expectation")
|
|
def step_verify_result(context: Context) -> None:
|
|
fixture = context.suite_fixture
|
|
result = context.suite_result
|
|
if fixture is None or result is None:
|
|
msg = "Fixture or result not set"
|
|
raise RuntimeError(msg)
|
|
|
|
verify_fixture_result(fixture, result)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixture-loading checks (FLAW-4 fix: validate schema, not just len > 0)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("all porting fixtures load without error")
|
|
def step_all_porting(context: Context) -> None:
|
|
fixtures = _get_fixtures(PORTING_FILENAME)
|
|
assert len(fixtures) > 0
|
|
for f in fixtures:
|
|
validate_fixture_schema(f)
|
|
|
|
|
|
@then("all dependency fixtures load without error")
|
|
def step_all_dependency(context: Context) -> None:
|
|
fixtures = _get_fixtures(DEPENDENCY_FILENAME)
|
|
assert len(fixtures) > 0
|
|
for f in fixtures:
|
|
validate_fixture_schema(f)
|
|
|
|
|
|
@then("all API surface fixtures load without error")
|
|
def step_all_api(context: Context) -> None:
|
|
fixtures = _get_fixtures(API_FILENAME)
|
|
assert len(fixtures) > 0
|
|
for f in fixtures:
|
|
validate_fixture_schema(f)
|
|
|
|
|
|
@then("all cross-file fixtures load without error")
|
|
def step_all_cross_file(context: Context) -> None:
|
|
fixtures = _get_fixtures(CROSS_FILE_FILENAME)
|
|
assert len(fixtures) > 0
|
|
for f in fixtures:
|
|
validate_fixture_schema(f)
|
|
|
|
|
|
@then("all circular import fixtures load without error")
|
|
def step_all_circular(context: Context) -> None:
|
|
fixtures = _get_fixtures(CIRCULAR_FILENAME)
|
|
assert len(fixtures) > 0
|
|
for f in fixtures:
|
|
validate_fixture_schema(f)
|