feat(admin-config-tool): tasks 5.1-5.3 - mapping/wire coverage confirmed, schema bundling fix
5.1 (mapping) and 5.2 (wire framing) are already fully satisfied by task 2.3's test_mapping.py and task 2.4's test_wire.py -- no new tests, would have been pure duplication. 5.3: test_schema_bundling.py cross-checks both bundling mechanisms (capnp schema, JSON validation schemas) resolve consistently under the active install, and found a real gap: a corrupted bundled JSON schema crashed validate_yaml() with a raw json.JSONDecodeError instead of a clean ValidationError. Fixed in xcecommon/validation.py.
This commit is contained in:
parent
ad27147a59
commit
b5040e0877
3 changed files with 152 additions and 4 deletions
137
src/xceadmin/tests/test_schema_bundling.py
Normal file
137
src/xceadmin/tests/test_schema_bundling.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
"""Cross-cutting bundling tests for ``xcecommon.schema`` and
|
||||
``xcecommon.validation`` (design.md, File Structure Plan; Requirements
|
||||
5.5, 7.4).
|
||||
|
||||
``xcecommon.schema``'s own missing/corrupted-``controller.capnp``
|
||||
handling is already exhaustively covered in ``test_schema.py`` (task
|
||||
2.2). This file's job is narrower: prove both bundling mechanisms
|
||||
(capnp schema, JSON validation schemas) resolve consistently under
|
||||
whichever install is currently active, and close the one confirmed gap
|
||||
task 5.3 found -- a corrupted *JSON* validation schema (as opposed to a
|
||||
corrupted ``controller.capnp``) crashed with a raw
|
||||
``json.JSONDecodeError`` instead of a clean ``ValidationError``, since
|
||||
``validate_yaml()``'s ``json.loads()`` call had no error handling. Fixed
|
||||
in ``xcecommon/validation.py`` alongside this test.
|
||||
|
||||
Meaningful confirmation of the pinned (``pip install .``) install path
|
||||
requires running this under the release build profile
|
||||
(``xce-build.sh xceadmin release``) -- under the default editable
|
||||
profile, resolution always succeeds by resolving into the checked-out
|
||||
source tree regardless of whether packaging is actually correct.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from xcecommon.errors import SchemaNotFoundError, ValidationError
|
||||
from xcecommon.schema import default_schema_path, load_schema
|
||||
from xcecommon.validation import default_schema_dir, validate_yaml
|
||||
|
||||
|
||||
def test_both_bundling_mechanisms_resolve_to_real_existing_paths():
|
||||
"""The capnp schema and the JSON validation schemas both resolve to
|
||||
real files under whichever install (editable or pinned) is currently
|
||||
active -- the single check that actually catches a packaging
|
||||
regression like the one bug-1460 found (missing package-data entry
|
||||
silently dropping schema_files/ from a non-editable wheel).
|
||||
"""
|
||||
capnp_path = Path(default_schema_path())
|
||||
assert capnp_path.is_file()
|
||||
|
||||
json_dir = Path(default_schema_dir())
|
||||
assert json_dir.is_dir()
|
||||
schema_files = list(json_dir.glob("*.schema.json"))
|
||||
assert len(schema_files) == 7 # one per config file type
|
||||
|
||||
|
||||
def test_both_mechanisms_produce_genuinely_parseable_content():
|
||||
mod = load_schema()
|
||||
assert hasattr(mod, "ClusterConfig")
|
||||
|
||||
json_dir = Path(default_schema_dir())
|
||||
for schema_file in json_dir.glob("*.schema.json"):
|
||||
with open(schema_file, encoding="utf-8") as fh:
|
||||
parsed = json.load(fh) # raises if genuinely not valid JSON
|
||||
assert isinstance(parsed, dict)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Corrupted / absent bundled schema -> clean error, not a crash
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def corrupted_json_schema():
|
||||
"""Temporarily corrupts the bundled xce.schema.json, restoring it
|
||||
afterward regardless of test outcome.
|
||||
"""
|
||||
target = Path(default_schema_dir()) / "xce.schema.json"
|
||||
backup = target.with_suffix(".json.bak")
|
||||
shutil.copy(str(target), str(backup))
|
||||
try:
|
||||
target.write_text("{not valid json!!!", encoding="utf-8")
|
||||
yield target
|
||||
finally:
|
||||
shutil.move(str(backup), str(target))
|
||||
|
||||
|
||||
def test_corrupted_json_schema_raises_validation_error_not_json_decode_error(
|
||||
corrupted_json_schema, tmp_path
|
||||
):
|
||||
"""The gap this task found: a corrupted bundled *JSON* schema (as
|
||||
opposed to a corrupted controller.capnp) previously crashed with a
|
||||
raw json.JSONDecodeError. validate_yaml() must convert it into a
|
||||
ValidationError instead (7.4's "clear, actionable error" principle,
|
||||
applied here to the JSON validation schemas).
|
||||
"""
|
||||
yaml_file = tmp_path / "xce.yaml"
|
||||
yaml_file.write_text("cluster_name: eda1\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
validate_yaml(str(yaml_file), "xce")
|
||||
|
||||
assert exc_info.value.constraint == "schema-corrupted"
|
||||
assert "corrupted" in exc_info.value.message
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def missing_json_schema():
|
||||
target = Path(default_schema_dir()) / "xce.schema.json"
|
||||
backup = target.with_suffix(".json.bak")
|
||||
shutil.move(str(target), str(backup))
|
||||
try:
|
||||
yield target
|
||||
finally:
|
||||
shutil.move(str(backup), str(target))
|
||||
|
||||
|
||||
def test_missing_json_schema_raises_validation_error_not_file_not_found_error(
|
||||
missing_json_schema, tmp_path
|
||||
):
|
||||
yaml_file = tmp_path / "xce.yaml"
|
||||
yaml_file.write_text("cluster_name: eda1\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
validate_yaml(str(yaml_file), "xce")
|
||||
|
||||
assert exc_info.value.constraint == "schema-not-found"
|
||||
|
||||
|
||||
def test_missing_controller_capnp_raises_schema_not_found_error(monkeypatch, tmp_path):
|
||||
"""Parallel case for the capnp side, using the env-var override
|
||||
(xcecommon.schema.load_schema doesn't cache the corrupted-content
|
||||
precheck failure, so this proves the same "clean error, not a
|
||||
crash" contract using the mechanism already established in
|
||||
test_schema.py -- included here for the cross-cutting file's own
|
||||
completeness, not as a duplicate of that file's exhaustive coverage.
|
||||
"""
|
||||
missing_path = tmp_path / "does-not-exist.capnp"
|
||||
monkeypatch.setenv("XCEADMIN_SCHEMA_PATH", str(missing_path))
|
||||
|
||||
with pytest.raises(SchemaNotFoundError):
|
||||
load_schema()
|
||||
Loading…
Add table
Add a link
Reference in a new issue