Files
shawn 9cd8292acc
Lint & Validate / lint (push) Has been cancelled
Phase 1-4: Audio stack, mixer engine, MIDI, and network API
P2-R1: ALSA + JACK2 low-latency config (scripts, quirks, tuning)
P2-R2: Carla integration (build scripts, 8ch rack config, NAM LV2 support)
P2-R3: Plugin manager, categories, blacklist, NAM model support
P3-R1: Mixer DSP engine (channel strip, routing matrix, bus mgr, automation)
P4-R1: MIDI engine (learn mode, clock sync, HID discovery, mapping store)
P4-R2: Network API (OSC server, FastAPI REST, WebSocket, auth, rate limiter)
P5-R1: Touchscreen UI evaluation + main entry point
docs: Audio stack, Carla integration, MIDI support, UI evaluation
tests: Full test suite (292 passing)
2026-05-19 20:39:17 -04:00

272 lines
8.0 KiB
Python

"""Tests for JSON schemas / Pydantic models."""
import json
import pytest
from datetime import datetime
from src.network.schemas import (
ChannelSchema,
MasterBusSchema,
MixerStateSchema,
PluginSchema,
TransportSchema,
RoutingSchema,
AuxBusSchema,
SubgroupSchema,
VCASchema,
EQSchema,
EQBandSchema,
CompressorSchema,
GateSchema,
FXSendsSchema,
ParameterUpdateSchema,
ParameterUpdateBatchSchema,
ParameterSetRequest,
WSMessage,
WSMessageType,
MixerParameterSchema,
ParameterCategoryEnum,
ParameterTypeEnum,
RouteNodeSchema,
RoutingEdgeSchema,
NodeTypeEnum,
APIResponse,
PluginParamMapSchema,
)
class TestChannelSchema:
"""Tests for ChannelSchema."""
def test_default_channel(self):
ch = ChannelSchema(channel=0)
assert ch.channel == 0
assert ch.volume_db == 0.0
assert ch.pan == 0.0
assert not ch.mute
assert not ch.solo
def test_channel_with_eq(self):
ch = ChannelSchema(
channel=3,
volume_db=-6.0,
eq=EQSchema(
enabled=True,
low=EQBandSchema(freq_hz=80.0, gain_db=3.0, q=1.0),
),
)
assert ch.eq.enabled
assert ch.eq.low.freq_hz == 80.0
assert ch.eq.low.gain_db == 3.0
def test_serialize_deserialize(self):
ch = ChannelSchema(channel=5, volume_db=-3.0, pan=0.5)
data = ch.model_dump()
ch2 = ChannelSchema.model_validate(data)
assert ch2.channel == 5
assert ch2.volume_db == -3.0
assert ch2.pan == 0.5
def test_json_roundtrip(self):
ch = ChannelSchema(channel=0, mute=True)
json_str = ch.model_dump_json()
data = json.loads(json_str)
assert data["channel"] == 0
assert data["mute"] is True
class TestMasterBusSchema:
"""Tests for MasterBusSchema."""
def test_default_master(self):
m = MasterBusSchema()
assert m.volume_db == 0.0
assert not m.muted
assert not m.dim_active
assert m.aux_buses == []
def test_with_buses(self):
m = MasterBusSchema(
volume_db=-10.0,
aux_buses=[
AuxBusSchema(index=0, label="Reverb"),
AuxBusSchema(index=1, label="Delay"),
],
subgroups=[
SubgroupSchema(index=0, label="Drums", members=[0, 1, 2]),
],
vca_groups=[
VCASchema(index=0, label="All Vocals", master_db=-3.0, members=[3, 4, 5]),
],
)
assert len(m.aux_buses) == 2
assert m.aux_buses[0].label == "Reverb"
assert len(m.subgroups) == 1
assert m.subgroups[0].members == [0, 1, 2]
assert m.vca_groups[0].master_db == -3.0
class TestTransportSchema:
"""Tests for TransportSchema."""
def test_defaults(self):
t = TransportSchema()
assert not t.playing
assert not t.recording
assert t.tempo_bpm == 120.0
assert t.position_sec == 0.0
def test_playing_state(self):
t = TransportSchema(playing=True, tempo_bpm=142.0, position_sec=35.2)
assert t.playing
assert not t.recording
assert t.tempo_bpm == 142.0
class TestMixerStateSchema:
"""Tests for full MixerStateSchema."""
def test_empty_state(self):
state = MixerStateSchema()
assert state.channels == []
assert state.master.volume_db == 0.0
assert state.transport.tempo_bpm == 120.0
def test_full_state(self):
state = MixerStateSchema(
channels=[
ChannelSchema(channel=0, volume_db=-6.0),
ChannelSchema(channel=1, mute=True),
],
master=MasterBusSchema(volume_db=-3.0),
transport=TransportSchema(playing=True),
scenes=["Intro", "Verse", "Chorus"],
uptime_seconds=3600.0,
)
assert len(state.channels) == 2
assert state.scenes == ["Intro", "Verse", "Chorus"]
assert state.uptime_seconds == 3600.0
def test_json_roundtrip(self):
state = MixerStateSchema(
channels=[ChannelSchema(channel=0)],
uptime_seconds=10.0,
)
json_str = state.model_dump_json()
data = json.loads(json_str)
assert data["uptime_seconds"] == 10.0
assert len(data["channels"]) == 1
class TestParameterUpdate:
"""Tests for parameter update schemas."""
def test_single_update(self):
update = ParameterUpdateSchema(
param_type=ParameterTypeEnum.VOLUME,
value=-3.0,
channel=2,
)
assert update.param_type == ParameterTypeEnum.VOLUME
assert update.value == -3.0
assert update.channel == 2
def test_batch_update(self):
batch = ParameterUpdateBatchSchema(updates=[
ParameterUpdateSchema(param_type=ParameterTypeEnum.VOLUME, value=-6.0, channel=0),
ParameterUpdateSchema(param_type=ParameterTypeEnum.MUTE, value=1.0, channel=0),
ParameterUpdateSchema(param_type=ParameterTypeEnum.MASTER_VOLUME, value=-3.0),
])
assert len(batch.updates) == 3
assert batch.updates[2].param_type == ParameterTypeEnum.MASTER_VOLUME
class TestWSMessage:
"""Tests for WebSocket message schemas."""
def test_parameter_update_message(self):
msg = WSMessage(
type=WSMessageType.PARAMETER_UPDATE,
payload={"param_type": "volume", "value": -3.0, "channel": 1},
)
assert msg.type == WSMessageType.PARAMETER_UPDATE
assert msg.payload["param_type"] == "volume"
def test_full_state_message(self):
msg = WSMessage(
type=WSMessageType.FULL_STATE,
payload={"channels": []},
)
data = msg.model_dump()
assert data["type"] == "full_state"
def test_error_message(self):
msg = WSMessage(
type=WSMessageType.ERROR,
payload={"message": "Something went wrong"},
)
assert msg.payload["message"] == "Something went wrong"
class TestParameterSetRequest:
"""Tests for ParameterSetRequest."""
def test_default_param_type(self):
req = ParameterSetRequest(value=-3.0)
assert req.param_type == "volume"
assert req.value == -3.0
def test_explicit_param_type(self):
req = ParameterSetRequest(param_type="mute", value=1.0)
assert req.param_type == "mute"
def test_json_parse(self):
json_str = '{"param_type": "pan", "value": 0.5}'
req = ParameterSetRequest.model_validate_json(json_str)
assert req.param_type == "pan"
assert req.value == 0.5
class TestPluginSchema:
"""Tests for PluginSchema."""
def test_plugin(self):
p = PluginSchema(
plugin_id=1,
name="Ch1 Gate",
role="gate",
channel=0,
params=[
PluginParamMapSchema(name="threshold", index=0, value=0.5),
PluginParamMapSchema(name="ratio", index=1, value=0.3),
],
)
assert p.plugin_id == 1
assert len(p.params) == 2
assert p.params[0].name == "threshold"
class TestRoutingSchema:
"""Tests for RoutingSchema."""
def test_empty_routing(self):
r = RoutingSchema()
assert r.nodes == []
assert r.edges == []
def test_with_nodes_and_edges(self):
r = RoutingSchema(
nodes=[
RouteNodeSchema(node_id="sys_in_1", type=NodeTypeEnum.SYSTEM_INPUT, jack_port="system:capture_1"),
RouteNodeSchema(node_id="ch_0_input", type=NodeTypeEnum.CHANNEL_INPUT, channel=0),
],
edges=[
RoutingEdgeSchema(source="sys_in_1", dest="ch_0_input"),
],
solo_nodes=["ch_0_input"],
)
assert len(r.nodes) == 2
assert len(r.edges) == 1
assert r.edges[0].source == "sys_in_1"
assert r.solo_nodes == ["ch_0_input"]