hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

commit 9b08be5aa42c77599b40e534bafd6e71e9b91aeb
parent f7517ee7413e989db0dcfc31063ae6c505e1ea61
Author: triesap <tyson@radroots.org>
Date:   Mon, 21 Sep 2026 03:41:28 +0000

spec: implement fixture structural validation

Diffstat:
Atests/fixture_validator.mojo | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/test_hyf.mojo | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 272 insertions(+), 0 deletions(-)

diff --git a/tests/fixture_validator.mojo b/tests/fixture_validator.mojo @@ -0,0 +1,200 @@ +from std.collections import List + +from std.os.path import exists +from std.pathlib import Path +from json import Value, loads + + +@fieldwise_init +struct FixtureValidationIssue(Copyable, Movable): + var case_id: String + var rule: String + var detail: String + + +def registered_fixture_operators() -> List[String]: + var operators = List[String]() + operators.append("equals") + operators.append("absent") + operators.append("present") + operators.append("contains") + operators.append("not_equals") + return operators^ + + +def _has_key(value: Value, key: String) -> Bool: + for candidate in value.object_keys(): + if candidate == key: + return True + return False + + +def _is_registered_operator( + operator: String, registered: List[String] +) -> Bool: + for candidate in registered: + if candidate == operator: + return True + return False + + +def validate_fixture_corpus( + directory: String, +) raises -> List[FixtureValidationIssue]: + var issues = List[FixtureValidationIssue]() + var base = Path(directory) + var manifest = loads((base / "manifest.json").read_text()) + if not _has_key(manifest, "cases"): + issues.append( + FixtureValidationIssue( + case_id="", rule="missing_cases", detail="manifest has no cases" + ) + ) + return issues^ + + var cases = manifest["cases"].array_items() + if len(cases) == 0: + issues.append( + FixtureValidationIssue( + case_id="", + rule="empty_corpus", + detail="manifest declares no cases", + ) + ) + + var seen = List[String]() + var registered = registered_fixture_operators() + for entry in cases: + var case_id = entry["case_id"].string_value() + + var duplicate = False + for prior in seen: + if prior == case_id: + duplicate = True + if duplicate: + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="duplicate_case_id", + detail="case_id appears more than once", + ) + ) + seen.append(String(case_id)) + + var relative_path = entry["path"].string_value() + var case_path = base / relative_path + if not exists(case_path): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="dangling_path", + detail=relative_path, + ) + ) + continue + + var doc = loads(case_path.read_text()) + if ( + not _has_key(doc, "case_id") + or doc["case_id"].string_value() != case_id + ): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="case_id_mismatch", + detail="case_id does not match manifest", + ) + ) + if ( + not _has_key(doc, "requirements") + or len(doc["requirements"].array_items()) == 0 + ): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="empty_requirements", + detail="no requirement references", + ) + ) + if ( + not _has_key(doc, "required_from_step") + or doc["required_from_step"].string_value() == "" + ): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="missing_activation_step", + detail="required_from_step is empty", + ) + ) + elif _has_key(entry, "required_from_step") and ( + doc["required_from_step"].string_value() + != entry["required_from_step"].string_value() + ): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="activation_step_mismatch", + detail="required_from_step does not match manifest", + ) + ) + if not _has_key(doc, "then") or len(doc["then"].array_items()) == 0: + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="empty_expectations", + detail="no expected assertions", + ) + ) + else: + for assertion in doc["then"].array_items(): + if not _has_key(assertion, "operator"): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="missing_operator", + detail="assertion has no operator", + ) + ) + continue + var operator = assertion["operator"].string_value() + if not _is_registered_operator(operator, registered): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="unknown_operator", + detail=operator, + ) + ) + if not _has_key(doc, "provenance"): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="missing_provenance", + detail="provenance is required", + ) + ) + if ( + not _has_key(doc, "implementation_status") + or doc["implementation_status"].string_value() != "planned" + ): + issues.append( + FixtureValidationIssue( + case_id=case_id, + rule="unexpected_status", + detail="new corpus cases must be planned until activated", + ) + ) + + if _has_key(manifest, "raw_files"): + for raw in manifest["raw_files"].array_items(): + if not exists(base / raw.string_value()): + issues.append( + FixtureValidationIssue( + case_id="", + rule="dangling_raw", + detail=raw.string_value(), + ) + ) + + return issues^ diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo @@ -26,6 +26,7 @@ from fixture_loader import ( load_fixture_scenario_request, load_fixture_top_level_field_from_path, ) +from fixture_validator import validate_fixture_corpus from hyf_core.backends.selector import ( execute_capability as execute_core_capability, resolve_backend, @@ -1198,5 +1199,76 @@ def test_semantic_fixture_corpus_is_installed_and_planned() raises: assert_true(exists(fixture_dir / raw.string_value())) +def _write_min_fixture_corpus(base: Path, mutation: String) raises: + var domain = base / "domain" + std.os.makedirs(domain.__fspath__(), exist_ok=True) + var case_text = ( + '{"fixture_format_version":1,"case_id":"T001",' + '"requirements":["HYF-TEST-001"],"implementation_status":"planned",' + '"required_from_step":"S009","mandatory":true,"given":{},' + '"provider_script":[],' + '"then":[{"operator":"equals","path":"/x","value":1}],' + '"provenance":{"kind":"synthetic"}}' + ) + if mutation == "unknown_operator": + case_text = case_text.replace('"equals"', '"not_registered"') + elif mutation == "empty_then": + case_text = case_text.replace( + '[{"operator":"equals","path":"/x","value":1}]', "[]" + ) + elif mutation == "missing_provenance": + case_text = case_text.replace(',"provenance":{"kind":"synthetic"}', "") + elif mutation == "empty_requirements": + case_text = case_text.replace('["HYF-TEST-001"]', "[]") + (domain / "T001.json").write_text(case_text) + + var case_entry = ( + '{"case_id":"T001","path":"domain/T001.json",' + '"required_from_step":"S009","mandatory":true}' + ) + if mutation == "duplicate_case_id": + case_entry = case_entry + "," + case_entry + elif mutation == "dangling_path": + case_entry = ( + '{"case_id":"T001","path":"domain/missing.json",' + '"required_from_step":"S009","mandatory":true}' + ) + elif mutation == "activation_step_mismatch": + case_entry = ( + '{"case_id":"T001","path":"domain/T001.json",' + '"required_from_step":"S999","mandatory":true}' + ) + var manifest = ( + '{"schema_version":1,"spec_id":"hyf_v1_jev",' + '"installation_status":"installed","cases":[' + + case_entry + + '],"raw_files":[]}' + ) + (base / "manifest.json").write_text(manifest) + + +def test_fixture_validator_accepts_corpus_and_rejects_corruptions() raises: + var corpus_dir = _dir_of_current_file() / "fixtures" / "hyf_v1_jev" + assert_equal( + len(validate_fixture_corpus(corpus_dir.__fspath__())), 0 + ) + var mutations = List[String]() + mutations.append("duplicate_case_id") + mutations.append("dangling_path") + mutations.append("unknown_operator") + mutations.append("empty_then") + mutations.append("missing_provenance") + mutations.append("empty_requirements") + mutations.append("activation_step_mismatch") + for mutation in mutations: + with TemporaryDirectory() as temp_dir: + var base = Path(temp_dir) + _write_min_fixture_corpus(base, mutation) + assert_true( + len(validate_fixture_corpus(base.__fspath__())) > 0, + "validator accepted corruption: " + mutation, + ) + + def main() raises: TestSuite.discover_tests[__functions_in_module()]().run()