import hashlib import importlib.util import json import tempfile import unittest from pathlib import Path def load_validate_reference_artifacts(): module_path = Path(__file__).resolve().parent / "validate_reference_artifacts.py" spec = importlib.util.spec_from_file_location("validate_reference_artifacts", module_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def write_json(path: Path, payload: dict): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, indent=2), encoding="utf-8") class ValidateReferenceArtifactsTests(unittest.TestCase): def test_missing_references_directory_is_valid(self): validator = load_validate_reference_artifacts() with tempfile.TemporaryDirectory() as tmp: self.assertEqual(validator.validate_root(Path(tmp)), []) def test_draft_metadata_with_minimal_provenance_is_valid(self): validator = load_validate_reference_artifacts() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) write_json( root / "references" / "umat" / "single-element" / "metadata.json", { "schema_version": "abaqus-user-subroutine-artifact-v1", "feature_id": "umat", "model_id": "single-element", "artifact_status": "draft", }, ) self.assertEqual(validator.validate_root(root), []) def test_ready_for_comparison_requires_declared_files(self): validator = load_validate_reference_artifacts() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) write_json( root / "references" / "umat" / "single-element" / "metadata.json", { "schema_version": "abaqus-user-subroutine-artifact-v1", "feature_id": "umat", "model_id": "single-element", "artifact_status": "ready-for-comparison", "abaqus": {"version": "2024", "precision": "double", "command": "abaqus job=case user=UMAT.for"}, "compiler": {"vendor": "Intel oneAPI", "name": "ifx", "version": "2024"}, "subroutine": { "entry_points": ["UMAT"], "source_files": [{"path": "src/fortran/abaqus/UMAT.for", "language": "Fortran", "sha256": "abc"}], }, "input_file": "model.inp", "outputs": { "tails": {"msg": "job.msg.tail.txt", "dat": "job.dat.tail.txt", "log": "job.log.tail.txt"}, "csv": {"stresses": "stresses.csv"}, }, }, ) errors = validator.validate_root(root) self.assertTrue(any("missing input_file" in error for error in errors)) self.assertTrue(any("missing output tail" in error for error in errors)) self.assertTrue(any("missing csv output" in error for error in errors)) self.assertTrue(any("missing source file" in error for error in errors)) def test_ready_for_comparison_checks_source_sha256(self): validator = load_validate_reference_artifacts() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) source = root / "src" / "fortran" / "abaqus" / "UMAT.for" source.parent.mkdir(parents=True) source.write_text(" subroutine umat()\n end\n", encoding="utf-8") source_hash = hashlib.sha256(source.read_bytes()).hexdigest() model_dir = root / "references" / "umat" / "single-element" for name in ["model.inp", "job.msg.tail.txt", "job.dat.tail.txt", "job.log.tail.txt", "stresses.csv"]: (model_dir / name).parent.mkdir(parents=True, exist_ok=True) (model_dir / name).write_text("ok\n", encoding="utf-8") write_json( model_dir / "metadata.json", { "schema_version": "abaqus-user-subroutine-artifact-v1", "feature_id": "umat", "model_id": "single-element", "artifact_status": "ready-for-comparison", "abaqus": {"version": "2024", "precision": "double", "command": "abaqus job=case user=UMAT.for"}, "compiler": {"vendor": "Intel oneAPI", "name": "ifx", "version": "2024"}, "subroutine": { "entry_points": ["UMAT"], "source_files": [ { "path": "src/fortran/abaqus/UMAT.for", "language": "Fortran", "sha256": "0" * len(source_hash), } ], }, "input_file": "model.inp", "outputs": { "tails": {"msg": "job.msg.tail.txt", "dat": "job.dat.tail.txt", "log": "job.log.tail.txt"}, "csv": {"stresses": "stresses.csv"}, }, }, ) errors = validator.validate_root(root) self.assertTrue(any("sha256 mismatch" in error for error in errors)) if __name__ == "__main__": unittest.main()