feat(player): Controls panel model (serial-contract data shape)

Sub-project 3 slice 1, per ROADMAP.md §3 and design §6/§7.
This commit is contained in:
Ben Stull
2026-06-05 18:06:27 -07:00
parent 70834ae0ad
commit 57597be8af
3 changed files with 121 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
import pytest
from player.controls import (
Controls,
CONTENT_POSITIONS,
ControlsError,
validate_controls,
parse_controls,
)
def test_content_positions_are_the_seven_from_the_spec():
assert CONTENT_POSITIONS == frozenset(
{"off", "white_noise", "music", "audio_track", "video", "music_video", "audio_video"}
)
def test_valid_controls_pass_validation():
c = Controls(content="video", left=0, right=4, dark=2, light=2, volume=3, brightness=4)
validate_controls(c) # does not raise
def test_invalid_content_position_rejected():
c = Controls(content="bogus", left=0, right=0, dark=0, light=0, volume=0, brightness=0)
with pytest.raises(ControlsError):
validate_controls(c)
@pytest.mark.parametrize("field", ["left", "right", "dark", "light", "volume", "brightness"])
@pytest.mark.parametrize("bad", [-1, 5, True])
def test_out_of_range_or_non_int_knob_rejected(field, bad):
kwargs = dict(content="off", left=0, right=0, dark=0, light=0, volume=0, brightness=0)
kwargs[field] = bad
with pytest.raises(ControlsError):
validate_controls(Controls(**kwargs))
def test_parse_controls_from_mapping():
c = parse_controls(
{"content": "music_video", "left": 1, "right": 2, "dark": 3, "light": 0, "volume": 2, "brightness": 1}
)
assert c == Controls("music_video", 1, 2, 3, 0, 2, 1)
def test_parse_controls_rejects_unknown_keys():
with pytest.raises(ControlsError):
parse_controls({"content": "off", "left": 0, "right": 0, "dark": 0,
"light": 0, "volume": 0, "brightness": 0, "bogus": 1})
def test_parse_controls_rejects_missing_keys():
with pytest.raises(ControlsError):
parse_controls({"content": "off", "left": 0})