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,17 @@
|
||||
{
|
||||
"Name": "EmptySteps",
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Name": "MissingRampRate",
|
||||
"pot": {
|
||||
"grain_mass": 0,
|
||||
"water_mass": 20
|
||||
},
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"hold": {
|
||||
"duration": 0
|
||||
},
|
||||
"ramp": {}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"descr": "Heat up",
|
||||
"temperature": 65
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"Name": "NoSteps",
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"Name": "WrongType",
|
||||
"pot": {
|
||||
"grain_mass": 0,
|
||||
"water_mass": 20
|
||||
},
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"descr": "Heat up",
|
||||
"temperature": 65,
|
||||
"ramp": {
|
||||
"rate": "fast"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"Name": "StepNotDict",
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
"not a step object"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"Name": "Minimal",
|
||||
"pot": {
|
||||
"grain_mass": 0,
|
||||
"water_mass": 20
|
||||
},
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"descr": "Only step"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Name": "MultiStep",
|
||||
"pot": {
|
||||
"grain_mass": 0,
|
||||
"water_mass": 22
|
||||
},
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"descr": "Aufheizen",
|
||||
"temperature": 57,
|
||||
"ramp": {
|
||||
"rate": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"descr": "Rast",
|
||||
"temperature": 63,
|
||||
"hold": {
|
||||
"duration": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"descr": "Ausmaischen",
|
||||
"temperature": 78,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Name": "WithTarget",
|
||||
"pot": {
|
||||
"grain_mass": 0,
|
||||
"water_mass": 20
|
||||
},
|
||||
"default": {
|
||||
"step": {
|
||||
"descr": "Put description here",
|
||||
"user_wait_for_continue": false,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"descr": "Heat up",
|
||||
"temperature": 65
|
||||
},
|
||||
{
|
||||
"descr": "Hold",
|
||||
"hold": {
|
||||
"duration": 20
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user