import json import os import re import unittest from utils.config_validate import ConfigError, validate_temp_ctrl FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixtures') # filename -> None if the fixture should pass validation, otherwise a regex # matched against the raised ConfigError's message. CASES = { 'valid_sim.json': None, 'valid_real.json': None, 'valid_normal.json': None, 'legacy_unknown_keys.json': None, 'missing_outer_kp.json': r'TempCtrl\.Outer\.kp', 'missing_inner_hold_branch.json': r'TempCtrl\.Inner\.Hold', 'missing_pid_type.json': r'TempCtrl\.pid_type', 'missing_tempctrl_section.json': r'missing required config key: TempCtrl$', 'wrong_type_kp_string.json': r'TempCtrl\.Inner\.Heat\.kp', 'wrong_type_tempctrl_not_dict.json': r'TempCtrl must be dict', } def _load(name): with open(os.path.join(FIXTURES_DIR, name)) as f: return json.load(f) def _test_name(filename): return 'test_' + re.sub(r'\W', '_', filename[:-len('.json')]) class TestConfigFixtures(unittest.TestCase): def test_every_fixture_file_is_covered(self): on_disk = {f for f in os.listdir(FIXTURES_DIR) if f.endswith('.json')} self.assertEqual(on_disk, set(CASES), "fixtures/ and CASES have drifted apart") def _make_fixture_test(name, expected_error): def test(self): config = _load(name) if expected_error is None: validate_temp_ctrl(config) # must not raise else: with self.assertRaisesRegex(ConfigError, expected_error): validate_temp_ctrl(config) return test # One dedicated test method per fixture file - not a single method looping # over CASES with subTest - so `unittest discover -v` (and any IDE test # runner) lists and can re-run each fixture individually instead of folding # them all into one opaque "did any of these fail" result. for _name, _expected_error in CASES.items(): setattr(TestConfigFixtures, _test_name(_name), _make_fixture_test(_name, _expected_error)) if __name__ == '__main__': unittest.main()