102 lines
3.5 KiB
Python
102 lines
3.5 KiB
Python
"""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))
|
|
|
|
|
|
# --- 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"
|
|
assert any(
|
|
e.validator == "minimum" and list(e.path) == ["price", "amount"]
|
|
for e in errs
|
|
), [(e.validator, list(e.path), e.message) for e in errs]
|
|
|
|
|
|
# --- 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]
|