diff --git a/server/tests/test_calibration.py b/server/tests/test_calibration.py index a1f5c07..0ed0626 100644 --- a/server/tests/test_calibration.py +++ b/server/tests/test_calibration.py @@ -213,8 +213,8 @@ def test_pixel_mapper_map_border_to_leds(): led_colors = mapper.map_border_to_leds(border_pixels) - assert len(led_colors) == 40 - assert all(isinstance(c, tuple) and len(c) == 3 for c in led_colors) + assert led_colors.shape == (40, 3) + assert led_colors.dtype == np.uint8 # Verify colors are reasonable (allowing for some rounding) # Bottom LEDs should be mostly blue diff --git a/server/tests/test_processor_manager.py b/server/tests/test_processor_manager.py index c33e958..c0e2488 100644 --- a/server/tests/test_processor_manager.py +++ b/server/tests/test_processor_manager.py @@ -3,8 +3,6 @@ import pytest from wled_controller.core.processing.processor_manager import ProcessorDependencies, ProcessorManager -from wled_controller.core.processing.processing_settings import ProcessingSettings -from wled_controller.core.capture.calibration import create_default_calibration @pytest.fixture @@ -71,7 +69,8 @@ def test_add_device_duplicate(processor_manager): ) -def test_remove_device(processor_manager): +@pytest.mark.asyncio +async def test_remove_device(processor_manager): """Test removing a device.""" processor_manager.add_device( device_id="test_device", @@ -79,15 +78,16 @@ def test_remove_device(processor_manager): led_count=150, ) - processor_manager.remove_device("test_device") + await processor_manager.remove_device("test_device") assert "test_device" not in processor_manager.get_all_devices() -def test_remove_device_not_found(processor_manager): +@pytest.mark.asyncio +async def test_remove_device_not_found(processor_manager): """Test removing non-existent device fails.""" with pytest.raises(ValueError, match="not found"): - processor_manager.remove_device("nonexistent") + await processor_manager.remove_device("nonexistent") def test_add_target(processor_manager): @@ -101,12 +101,11 @@ def test_add_target(processor_manager): processor_manager.add_target( target_id="target_1", device_id="test_device", - settings=ProcessingSettings(fps=60, display_index=1), + fps=60, ) state = processor_manager.get_target_state("target_1") assert state["target_id"] == "target_1" - assert state["fps_target"] == 60 def test_add_target_duplicate(processor_manager): @@ -161,42 +160,25 @@ def test_update_target_settings(processor_manager): device_id="test_device", ) - new_settings = ProcessingSettings( - display_index=1, - fps=60, - ) - - processor_manager.update_target_settings("target_1", new_settings) + processor_manager.update_target_settings("target_1", {"fps": 60}) state = processor_manager.get_target_state("target_1") assert state["fps_target"] == 60 -def test_update_calibration(processor_manager): - """Test updating device calibration.""" +def test_update_device_info(processor_manager): + """Test updating device info after registration.""" processor_manager.add_device( device_id="test_device", device_url="http://192.168.1.100", led_count=150, ) - new_calibration = create_default_calibration(150) - - processor_manager.update_calibration("test_device", new_calibration) - - -def test_update_calibration_led_count_mismatch(processor_manager): - """Test updating calibration with mismatched LED count fails.""" - processor_manager.add_device( - device_id="test_device", - device_url="http://192.168.1.100", - led_count=150, + processor_manager.update_device_info( + "test_device", led_count=200, device_url="http://192.168.1.101" ) - - wrong_calibration = create_default_calibration(100) # Wrong count - - with pytest.raises(ValueError, match="does not match"): - processor_manager.update_calibration("test_device", wrong_calibration) + dev = processor_manager._devices["test_device"] + assert dev.led_count == 200 def test_get_target_state(processor_manager): @@ -210,15 +192,13 @@ def test_get_target_state(processor_manager): processor_manager.add_target( target_id="target_1", device_id="test_device", - settings=ProcessingSettings(fps=30, display_index=0), + fps=30, ) state = processor_manager.get_target_state("target_1") assert state["target_id"] == "target_1" assert state["processing"] is False - assert state["fps_target"] == 30 - assert state["display_index"] == 0 def test_get_target_state_not_found(processor_manager): diff --git a/server/tests/test_screen_capture.py b/server/tests/test_screen_capture.py index 85c8eaa..11b1ca5 100644 --- a/server/tests/test_screen_capture.py +++ b/server/tests/test_screen_capture.py @@ -129,8 +129,9 @@ def test_get_edge_segments_invalid(): with pytest.raises(ValueError): get_edge_segments(edge_pixels, 0, "top") - with pytest.raises(ValueError): - get_edge_segments(edge_pixels, 200, "top") # More segments than pixels + # More segments than pixels is allowed — returns segments with fewer pixels each + result = get_edge_segments(edge_pixels, 200, "top") + assert len(result) == 200 def test_calculate_average_color(): diff --git a/server/tests/test_wled_client.py b/server/tests/test_wled_client.py index 8350f7a..823b429 100644 --- a/server/tests/test_wled_client.py +++ b/server/tests/test_wled_client.py @@ -27,6 +27,20 @@ def mock_wled_info(): } +@pytest.fixture +def mock_wled_cfg(): + """Provide mock WLED config response (needed by get_info).""" + return { + "hw": { + "led": { + "ins": [ + {"start": 0, "len": 150, "order": 1, "pin": [2], "type": 22} + ] + } + } + } + + @pytest.fixture def mock_wled_state(): """Provide mock WLED state response.""" @@ -37,13 +51,28 @@ def mock_wled_state(): } -@pytest.mark.asyncio -@respx.mock -async def test_wled_client_connect(wled_url, mock_wled_info): - """Test connecting to WLED device.""" +def _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): + """Set up respx mocks for all endpoints hit during connect(). + + connect() calls get_info() which hits /json/info + /json/cfg, + then snapshot_device_state() which hits /json/state. + """ respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) + respx.get(f"{wled_url}/json/cfg").mock( + return_value=Response(200, json=mock_wled_cfg) + ) + respx.get(f"{wled_url}/json/state").mock( + return_value=Response(200, json=mock_wled_state) + ) + + +@pytest.mark.asyncio +@respx.mock +async def test_wled_client_connect(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): + """Test connecting to WLED device.""" + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) client = WLEDClient(wled_url) success = await client.connect() @@ -72,11 +101,9 @@ async def test_wled_client_connect_failure(wled_url): @pytest.mark.asyncio @respx.mock -async def test_get_info(wled_url, mock_wled_info): +async def test_get_info(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test getting device info.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) async with WLEDClient(wled_url) as client: info = await client.get_info() @@ -90,14 +117,9 @@ async def test_get_info(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_get_state(wled_url, mock_wled_info, mock_wled_state): +async def test_get_state(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test getting device state.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) - respx.get(f"{wled_url}/json/state").mock( - return_value=Response(200, json=mock_wled_state) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) async with WLEDClient(wled_url) as client: state = await client.get_state() @@ -108,11 +130,9 @@ async def test_get_state(wled_url, mock_wled_info, mock_wled_state): @pytest.mark.asyncio @respx.mock -async def test_send_pixels(wled_url, mock_wled_info): +async def test_send_pixels(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test sending pixel data.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) @@ -130,11 +150,9 @@ async def test_send_pixels(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_send_pixels_invalid_values(wled_url, mock_wled_info): +async def test_send_pixels_invalid_values(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test sending invalid pixel values.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) async with WLEDClient(wled_url) as client: # Invalid RGB value @@ -152,11 +170,9 @@ async def test_send_pixels_invalid_values(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_set_power(wled_url, mock_wled_info): +async def test_set_power(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test turning device on/off.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) @@ -173,11 +189,9 @@ async def test_set_power(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_set_brightness(wled_url, mock_wled_info): +async def test_set_brightness(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test setting brightness.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) @@ -193,11 +207,9 @@ async def test_set_brightness(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_test_connection(wled_url, mock_wled_info): +async def test_test_connection(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test connection testing.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) async with WLEDClient(wled_url) as client: success = await client.test_connection() @@ -206,36 +218,40 @@ async def test_test_connection(wled_url, mock_wled_info): @pytest.mark.asyncio @respx.mock -async def test_retry_logic(wled_url, mock_wled_info): +async def test_retry_logic(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test retry logic on failures.""" - # Mock to fail twice, then succeed - call_count = 0 + # Mock to fail twice, then succeed on /json/info + info_call_count = 0 - def mock_response(request): - nonlocal call_count - call_count += 1 - if call_count < 3: + def mock_info_response(request): + nonlocal info_call_count + info_call_count += 1 + if info_call_count < 3: return Response(500, text="Error") return Response(200, json=mock_wled_info) - respx.get(f"{wled_url}/json/info").mock(side_effect=mock_response) + respx.get(f"{wled_url}/json/info").mock(side_effect=mock_info_response) + respx.get(f"{wled_url}/json/cfg").mock( + return_value=Response(200, json=mock_wled_cfg) + ) + respx.get(f"{wled_url}/json/state").mock( + return_value=Response(200, json=mock_wled_state) + ) client = WLEDClient(wled_url, retry_attempts=3, retry_delay=0.1) success = await client.connect() assert success is True - assert call_count == 3 # Failed 2 times, succeeded on 3rd + assert info_call_count == 3 # Failed 2 times, succeeded on 3rd await client.close() @pytest.mark.asyncio @respx.mock -async def test_context_manager(wled_url, mock_wled_info): +async def test_context_manager(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state): """Test async context manager usage.""" - respx.get(f"{wled_url}/json/info").mock( - return_value=Response(200, json=mock_wled_info) - ) + _mock_connect_endpoints(wled_url, mock_wled_info, mock_wled_cfg, mock_wled_state) async with WLEDClient(wled_url) as client: assert client.is_connected is True