import json import os import re import unittest from utils.config_validate import ConfigError from utils.sud_validate import validate_sud FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'sud_fixtures') SUDE_DIR = os.path.join(os.path.dirname(__file__), '..', '..', 'sude') # filename -> None if the fixture should pass validation, otherwise a regex # matched against the raised ConfigError's message. CASES = { 'valid_minimal.json': None, 'valid_with_target.json': None, 'valid_multi_step.json': None, 'missing_steps_key.json': r"missing required config key: steps", 'empty_steps_list.json': r"'steps' must be a non-empty list", 'missing_ramp_rate.json': r'step 1 \(Heat up\): .*ramp\.rate', 'ramp_rate_wrong_type.json': r'step 1 \(Heat up\): .*ramp\.rate', 'step_not_dict.json': r'malformed sud document', } def _load(directory, name): with open(os.path.join(directory, name)) as f: return json.load(f) def _test_name(filename): return 'test_' + re.sub(r'\W', '_', filename[:-len('.json')]) class TestSudValidateFixtures(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), "sud_fixtures/ and CASES have drifted apart") def _make_fixture_test(name, expected_error): def test(self): data = _load(FIXTURES_DIR, name) if expected_error is None: validate_sud(data) # must not raise else: with self.assertRaisesRegex(ConfigError, expected_error): validate_sud(data) 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(TestSudValidateFixtures, _test_name(_name), _make_fixture_test(_name, _expected_error)) class TestRealSudFiles(unittest.TestCase): """The actual sude/*.json schedules must always pass - a failure here means either a real schedule is broken, or validate_sud() itself has drifted from what Sud._parse_data() actually accepts.""" def _make_real_file_test(name): def test(self): validate_sud(_load(SUDE_DIR, name)) return test _real_sud_files = [f for f in os.listdir(SUDE_DIR) if f.endswith('.json')] assert _real_sud_files, "expected at least one sude/*.json file" for _name in _real_sud_files: setattr(TestRealSudFiles, _test_name(_name), _make_real_file_test(_name)) if __name__ == '__main__': unittest.main()