"""Schema validation tests for schemas/solution.schema.json and schemas/listing.schema.json. Frozen v1 schemas (specs/001-market-first-wedge/data-model.md) — do not modify. """ import json from pathlib import Path import pytest from jsonschema import Draft202012Validator SCHEMAS_DIR = Path(__file__).resolve().parent.parent FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures" def load(path): with open(path) as f: return json.load(f) SOLUTION_SCHEMA = load(SCHEMAS_DIR / "solution.schema.json") LISTING_SCHEMA = load(SCHEMAS_DIR / "listing.schema.json") solution_validator = Draft202012Validator(SOLUTION_SCHEMA) listing_validator = Draft202012Validator(LISTING_SCHEMA) def fixture(name): return load(FIXTURES_DIR / name) def errors(validator, instance): return list(validator.iter_errors(instance)) def flatten_errors(errs): flat = [] stack = list(errs) while stack: err = stack.pop() flat.append(err) stack.extend(err.context) return flat # --- valid fixtures --------------------------------------------------------- def test_valid_solution_passes(): errs = errors(solution_validator, fixture("valid_solution.json")) assert errs == [], [e.message for e in errs] def test_valid_listing_passes(): errs = errors(listing_validator, fixture("valid_listing.json")) assert errs == [], [e.message for e in errs] # --- invalid solution fixtures ---------------------------------------------- def test_solution_missing_evidence_fails_on_evidence_minitems(): errs = errors(solution_validator, fixture("solution_missing_evidence.json")) assert errs, "expected validation errors" assert any( e.validator == "minItems" and list(e.path) == ["evidence"] for e in errs ), [(e.validator, list(e.path), e.message) for e in errs] def test_solution_tenant_violation_fails_on_else_branch(): errs = errors(solution_validator, fixture("solution_tenant_violation.json")) assert errs, "expected validation errors" # tenant_scope != "m2-core" routes through the top-level if/else; the # else branch requires owner_initiated + scrub_status, both absent here. # jsonschema surfaces these as top-level "required" errors (path == []). missing_required_props = { prop for e in errs if e.validator == "required" and list(e.path) == [] for prop in ("owner_initiated", "scrub_status") if f"'{prop}'" in e.message } assert missing_required_props == {"owner_initiated", "scrub_status"}, [ (e.validator, list(e.path), e.message) for e in errs ] def test_solution_bad_price_fails_on_price_amount_minimum(): errs = errors(solution_validator, fixture("solution_bad_price.json")) assert errs, "expected validation errors" flat = flatten_errors(errs) assert any( e.validator == "minimum" and list(e.path) in (["price", "amount"], ["amount"]) for e in flat ), [(e.validator, list(e.path), e.message) for e in flat] # --- invalid listing fixtures ------------------------------------------------ def test_listing_bad_status_fails_on_status_enum(): errs = errors(listing_validator, fixture("listing_bad_status.json")) assert errs, "expected validation errors" assert any( e.validator == "enum" and list(e.path) == ["status"] for e in errs ), [(e.validator, list(e.path), e.message) for e in errs] def test_listing_missing_tenant_visibility_fails_on_required(): errs = errors(listing_validator, fixture("listing_missing_tenant_visibility.json")) assert errs, "expected validation errors" assert any( e.validator == "required" and "tenant_visibility" in e.message and list(e.path) == [] for e in errs ), [(e.validator, list(e.path), e.message) for e in errs]