sud: fail fast on malformed sud.json documents, one test per fixture
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
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
from utils.config_validate import ConfigError, require
|
||||
from components.sud import Sud
|
||||
|
||||
|
||||
def validate_sud(data):
|
||||
"""Fail-fast structural check for a sud.json document - same rationale
|
||||
as config_validate.py's validate_temp_ctrl(): a malformed field here
|
||||
doesn't surface until deep inside a run, or worse, during
|
||||
SudForecastEstimator's very first Load-time simulation (tasks/sud.py,
|
||||
components/sud_forecast.py and scripts/demos/sud/demo_sud.py all read
|
||||
ramp['rate'] directly, no .get() fallback, the moment any step actually
|
||||
sets its own 'temperature') - as a bare KeyError with no indication of
|
||||
which schedule step or field is wrong.
|
||||
|
||||
Reuses Sud._parse_data() itself (rather than re-implementing default-
|
||||
merging/step-building) so what's checked here can never drift from what
|
||||
actually gets parsed at runtime."""
|
||||
require(data, 'steps', type=list)
|
||||
if not data['steps']:
|
||||
raise ConfigError("'steps' must be a non-empty list")
|
||||
|
||||
try:
|
||||
schedule = Sud._parse_data(data)[2]
|
||||
except (KeyError, TypeError, AttributeError) as e:
|
||||
raise ConfigError("malformed sud document: {}".format(e))
|
||||
|
||||
for step in schedule:
|
||||
if step['temperature'] is None:
|
||||
continue
|
||||
try:
|
||||
require(step, 'ramp.rate', type=(int, float))
|
||||
except ConfigError as e:
|
||||
raise ConfigError("step {} ({}): {}".format(step['number'], step.get('descr'), e))
|
||||
Reference in New Issue
Block a user