Sud.load() now calls utils/sud_validate.py's validate_sud() before parsing: requires a non-empty 'steps' list, and for every resolved step that sets its own 'temperature', a numeric ramp.rate - the one genuinely unguarded crash site (ramp['rate'], no .get() fallback) hit in tasks/sud.py, components/sud_forecast.py and demo_sud.py the moment that step is reached, which for SudForecastEstimator can be as early as the Load itself. A refused load is now logged with the specific reason (previously silent). validate_sud() reuses Sud._parse_data() directly rather than reimplementing default-merging/step-building. tests/utils/test_sud_validate_fixtures.py covers 8 fixture docs plus all 6 real sude/*.json files; tests/components/sud/test_sud_load.py exercises the Sud.load() wiring itself (rejects, keeps prior schedule). Also reworked both this and test_config_fixtures.py to generate one dedicated test method per fixture file (dynamic setattr) instead of looping with subTest inside a single method - subTest iterations don't show up individually in `unittest -v`/IDE test runners, which made it look like far fewer fixtures were covered than actually are. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4oEE99rkKVU8L8tkzXWdG
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
import json
|
|
import os
|
|
import unittest
|
|
|
|
from components.sud import Sud
|
|
|
|
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), '..', '..', 'utils', 'sud_fixtures')
|
|
|
|
|
|
def _load_fixture(name):
|
|
with open(os.path.join(FIXTURES_DIR, name)) as f:
|
|
return json.load(f)
|
|
|
|
|
|
class TestSudLoadRejectsMalformedDocs(unittest.TestCase):
|
|
"""Sud.load() wires in utils/sud_validate.py's validate_sud() so a
|
|
malformed sud.json is refused (same False-returning contract as any
|
|
other load() failure - see components/sud.py's own docstring) instead
|
|
of parsing cleanly and only crashing much later, mid-run or during a
|
|
forecast simulation, on the unguarded ramp['rate'] lookup."""
|
|
|
|
def test_valid_doc_loads(self):
|
|
sud = Sud()
|
|
self.assertTrue(sud.load(_load_fixture('valid_multi_step.json')))
|
|
self.assertEqual(sud.name, 'MultiStep')
|
|
|
|
def test_missing_ramp_rate_is_refused(self):
|
|
sud = Sud()
|
|
self.assertFalse(sud.load(_load_fixture('missing_ramp_rate.json')))
|
|
|
|
def test_wrong_type_ramp_rate_is_refused(self):
|
|
sud = Sud()
|
|
self.assertFalse(sud.load(_load_fixture('ramp_rate_wrong_type.json')))
|
|
|
|
def test_empty_steps_is_refused(self):
|
|
sud = Sud()
|
|
self.assertFalse(sud.load(_load_fixture('empty_steps_list.json')))
|
|
|
|
def test_failed_load_leaves_previous_schedule_untouched(self):
|
|
sud = Sud()
|
|
self.assertTrue(sud.load(_load_fixture('valid_multi_step.json')))
|
|
self.assertFalse(sud.load(_load_fixture('missing_ramp_rate.json')))
|
|
self.assertEqual(sud.name, 'MultiStep')
|
|
self.assertEqual(len(sud.schedule), 3)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|