|
| 1 | +"""Formal tests for the audit-confirmed bugs and their fixes. |
| 2 | +
|
| 3 | +These tests serve as formal validation that: |
| 4 | +1. _observation_pair_failures fails closed on malformed JSON (Bug 1a) |
| 5 | +2. _usage rejects non-dict stats (Bug 1b) |
| 6 | +3. _load_registry_cache provides invalidation (Bug 3a) |
| 7 | +4. install_source_only_importer purges sys.modules (Bug TOCTOU) |
| 8 | +""" |
| 9 | +import json |
| 10 | +import sys |
| 11 | +from pathlib import Path |
| 12 | +from unittest.mock import patch |
| 13 | + |
| 14 | +import pytest |
| 15 | + |
| 16 | + |
| 17 | +# Bug 1a: _observation_pair_failures should fail closed on non-dict JSON |
| 18 | +def test_observation_pair_failures_fails_closed_on_non_dict(tmp_path): |
| 19 | + """Formally prove that malformed observation JSON is not silently accepted.""" |
| 20 | + from benchmarks.tooling import benchmark_contracts |
| 21 | + |
| 22 | + # When treatment/control JSON is a valid JSON but not a dict (e.g., a list), |
| 23 | + # _observation_pair_failures should return failures, not empty list. |
| 24 | + treatment_path = Path(benchmark_contracts.BENCHMARKS) / "datasets" / "mathematical-benchmarks-v1" / "jobs" / "jacobian-observation.json" |
| 25 | + control_path = Path(benchmark_contracts.BENCHMARKS) / "config" / "mathematical-benchmarks-v1-control.json" |
| 26 | + |
| 27 | + # Mock _read_json to return non-dict values |
| 28 | + original_read_json = benchmark_contracts._read_json |
| 29 | + |
| 30 | + try: |
| 31 | + # Test: JSON array instead of object |
| 32 | + def mock_read_json_array(path): |
| 33 | + return [] |
| 34 | + benchmark_contracts._read_json = mock_read_json_array |
| 35 | + failures = benchmark_contracts._observation_pair_failures() |
| 36 | + assert len(failures) > 0, "Bug 1a: malformed JSON should not silently pass" |
| 37 | + assert "malformed" in failures[0].lower() |
| 38 | + |
| 39 | + # Test: JSON null |
| 40 | + def mock_read_json_null(path): |
| 41 | + return None |
| 42 | + benchmark_contracts._read_json = mock_read_json_null |
| 43 | + failures = benchmark_contracts._observation_pair_failures() |
| 44 | + assert len(failures) > 0, "Bug 1a: null JSON should not silently pass" |
| 45 | + finally: |
| 46 | + benchmark_contracts._read_json = original_read_json |
| 47 | + |
| 48 | + |
| 49 | +# Bug 1b: _usage should reject non-dict stats |
| 50 | +def test_usage_rejects_non_dict_stats(): |
| 51 | + """Formally prove that non-dict stats is rejected.""" |
| 52 | + from benchmarks.tooling import heldout_runner |
| 53 | + |
| 54 | + # Create a temporary result file with stats as null |
| 55 | + import tempfile |
| 56 | + with tempfile.NamedTemporaryFile(suffix=".json", mode="w", delete=False) as f: |
| 57 | + json.dump({"stats": None}, f) |
| 58 | + path = Path(f.name) |
| 59 | + |
| 60 | + try: |
| 61 | + with pytest.raises(Exception) as exc_info: |
| 62 | + heldout_runner._usage(path) |
| 63 | + assert "stats" in str(exc_info.value).lower() |
| 64 | + finally: |
| 65 | + path.unlink(missing_ok=True) |
| 66 | + |
| 67 | + |
| 68 | +# Bug 3a: Registry cache should be invalidatable |
| 69 | +def test_registry_cache_invalidation(): |
| 70 | + """Formally prove that cache invalidation works.""" |
| 71 | + from benchmarks.tooling import harbor_suite |
| 72 | + |
| 73 | + assert hasattr(harbor_suite, "invalidate_registry_cache"), "invalidate_registry_cache should exist" |
| 74 | + harbor_suite.invalidate_registry_cache() |
| 75 | + assert harbor_suite._load_registry_cache == {}, "Cache should be empty after invalidation" |
| 76 | + |
| 77 | + |
| 78 | +# Bug TOCTOU: install_source_only_importer should purge sys.modules |
| 79 | +def test_source_only_importer_purges_sys_modules(): |
| 80 | + """Formally prove that pre-imported modules are purged.""" |
| 81 | + import importlib |
| 82 | + from jacobian.implementation import install_source_only_importer |
| 83 | + |
| 84 | + # Create a fake module in sys.modules that looks like the target package |
| 85 | + fake_module = type(sys)("fake_test_package") |
| 86 | + fake_module.some_value = "stale" |
| 87 | + sys.modules["fake_test_package"] = fake_module |
| 88 | + sys.modules["fake_test_package.helper"] = type(sys)("fake_test_package.helper") |
| 89 | + |
| 90 | + try: |
| 91 | + install_source_only_importer("fake_test_package:main") |
| 92 | + assert "fake_test_package" not in sys.modules, "Pre-imported module should be purged" |
| 93 | + assert "fake_test_package.helper" not in sys.modules, "Pre-imported submodule should be purged" |
| 94 | + finally: |
| 95 | + sys.modules.pop("fake_test_package", None) |
| 96 | + sys.modules.pop("fake_test_package.helper", None) |
| 97 | + # Clean up meta_path |
| 98 | + from jacobian.implementation import _SourceOnlyFinder |
| 99 | + sys.meta_path = [f for f in sys.meta_path if not isinstance(f, _SourceOnlyFinder)] |
| 100 | + |
| 101 | + |
| 102 | +if __name__ == "__main__": |
| 103 | + test_observation_pair_failures_fails_closed_on_non_dict(None) |
| 104 | + print("Bug 1a test passed") |
| 105 | + test_usage_rejects_non_dict_stats() |
| 106 | + print("Bug 1b test passed") |
| 107 | + test_registry_cache_invalidation() |
| 108 | + print("Bug 3a test passed") |
| 109 | + test_source_only_importer_purges_sys_modules() |
| 110 | + print("TOCTOU test passed") |
| 111 | + print("\nAll audit fix tests passed!") |
0 commit comments