76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
import json
|
|
import pytest
|
|
|
|
from h3_guidemaster.core.state import default_timeline_state, parse_timeline_state
|
|
|
|
|
|
def test_default_state_is_current_h3_24fps_and_empty():
|
|
state = default_timeline_state()
|
|
assert state.version == 2
|
|
assert state.fps == 24
|
|
assert state.guides == ()
|
|
assert not hasattr(state, "mask")
|
|
|
|
|
|
def test_parse_v2_timeline_normalizes_guides():
|
|
raw = json.dumps({
|
|
"version": 2,
|
|
"fps": 24,
|
|
"timeline": {"frame_count": 124, "selected_frame": 17},
|
|
"guides": [
|
|
{"id": "g1", "enabled": True, "frame": -1, "image_slot": 2, "audio_slot": None}
|
|
],
|
|
})
|
|
state = parse_timeline_state(raw)
|
|
assert state.version == 2
|
|
assert state.guides[0].frame == -1
|
|
assert state.guides[0].image_slot == 2
|
|
assert not hasattr(state.guides[0], "intent")
|
|
|
|
|
|
def test_parse_v1_migrates_and_drops_legacy_guide_intent():
|
|
state = parse_timeline_state(json.dumps({
|
|
"version": 1,
|
|
"fps": 24,
|
|
"reference": {"selected_frame": 88},
|
|
"guides": [{"id": "old", "frame": 24, "image_slot": 0, "intent": "legacy beat"}],
|
|
}))
|
|
assert state.version == 2
|
|
assert state.guides[0].id == "old"
|
|
assert state.guides[0].frame == 24
|
|
assert not hasattr(state.guides[0], "intent")
|
|
|
|
|
|
def test_parse_timeline_accepts_zero_based_autogrow_slots():
|
|
state = parse_timeline_state(json.dumps({
|
|
"version": 2,
|
|
"fps": 24,
|
|
"guides": [{"frame": 0, "image_slot": 0, "audio_slot": None}],
|
|
}))
|
|
assert state.guides[0].image_slot == 0
|
|
|
|
|
|
def test_rejects_unknown_state_version():
|
|
with pytest.raises(ValueError, match="timeline version"):
|
|
parse_timeline_state('{"version": 99, "fps": 24}')
|
|
|
|
|
|
def test_rejects_non_h3_fps():
|
|
with pytest.raises(ValueError, match="24 fps"):
|
|
parse_timeline_state('{"version": 2, "fps": 30}')
|
|
|
|
|
|
@pytest.mark.parametrize("payload", [
|
|
'{"version": 2.5, "fps": 24}',
|
|
'{"version": 2, "fps": 24, "guides": [{"frame": 1.5}]}',
|
|
'{"version": 2, "fps": 24, "guides": [{"frame": 1, "image_slot": 0.5}]}',
|
|
])
|
|
def test_rejects_non_integer_timeline_values(payload):
|
|
with pytest.raises(ValueError, match="integer"):
|
|
parse_timeline_state(payload)
|
|
|
|
|
|
def test_rejects_string_boolean_values():
|
|
with pytest.raises(ValueError, match="enabled"):
|
|
parse_timeline_state('{"version": 2, "fps": 24, "guides": [{"enabled": "false"}]}')
|