"""Tests for WLED client.""" import pytest import respx from httpx import Response from wled_controller.core.wled_client import WLEDClient, WLEDInfo @pytest.fixture def wled_url(): """Provide test WLED device URL.""" return "http://192.168.1.100" @pytest.fixture def mock_wled_info(): """Provide mock WLED info response.""" return { "name": "Test WLED", "ver": "0.14.0", "leds": {"count": 150}, "brand": "WLED", "product": "FOSS", "mac": "AA:BB:CC:DD:EE:FF", "ip": "192.168.1.100", } @pytest.fixture def mock_wled_state(): """Provide mock WLED state response.""" return { "on": True, "bri": 255, "seg": [{"id": 0, "on": True}], } @pytest.mark.asyncio @respx.mock async def test_wled_client_connect(wled_url, mock_wled_info): """Test connecting to WLED device.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) client = WLEDClient(wled_url) success = await client.connect() assert success is True assert client.is_connected is True await client.close() @pytest.mark.asyncio @respx.mock async def test_wled_client_connect_failure(wled_url): """Test connection failure handling.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(500, text="Internal Server Error") ) client = WLEDClient(wled_url, retry_attempts=1) with pytest.raises(RuntimeError): await client.connect() assert client.is_connected is False @pytest.mark.asyncio @respx.mock async def test_get_info(wled_url, mock_wled_info): """Test getting device info.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) async with WLEDClient(wled_url) as client: info = await client.get_info() assert isinstance(info, WLEDInfo) assert info.name == "Test WLED" assert info.version == "0.14.0" assert info.led_count == 150 assert info.mac == "AA:BB:CC:DD:EE:FF" @pytest.mark.asyncio @respx.mock async def test_get_state(wled_url, mock_wled_info, 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) ) async with WLEDClient(wled_url) as client: state = await client.get_state() assert state["on"] is True assert state["bri"] == 255 @pytest.mark.asyncio @respx.mock async def test_send_pixels(wled_url, mock_wled_info): """Test sending pixel data.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) async with WLEDClient(wled_url) as client: pixels = [ (255, 0, 0), # Red (0, 255, 0), # Green (0, 0, 255), # Blue ] success = await client.send_pixels(pixels, brightness=200) assert success is True @pytest.mark.asyncio @respx.mock async def test_send_pixels_invalid_values(wled_url, mock_wled_info): """Test sending invalid pixel values.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) async with WLEDClient(wled_url) as client: # Invalid RGB value with pytest.raises(ValueError): await client.send_pixels([(300, 0, 0)]) # Invalid brightness with pytest.raises(ValueError): await client.send_pixels([(255, 0, 0)], brightness=300) # Empty pixels list with pytest.raises(ValueError): await client.send_pixels([]) @pytest.mark.asyncio @respx.mock async def test_set_power(wled_url, mock_wled_info): """Test turning device on/off.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) async with WLEDClient(wled_url) as client: # Turn on success = await client.set_power(True) assert success is True # Turn off success = await client.set_power(False) assert success is True @pytest.mark.asyncio @respx.mock async def test_set_brightness(wled_url, mock_wled_info): """Test setting brightness.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) respx.post(f"{wled_url}/json/state").mock( return_value=Response(200, json={"success": True}) ) async with WLEDClient(wled_url) as client: success = await client.set_brightness(128) assert success is True # Invalid brightness with pytest.raises(ValueError): await client.set_brightness(300) @pytest.mark.asyncio @respx.mock async def test_test_connection(wled_url, mock_wled_info): """Test connection testing.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) async with WLEDClient(wled_url) as client: success = await client.test_connection() assert success is True @pytest.mark.asyncio @respx.mock async def test_retry_logic(wled_url, mock_wled_info): """Test retry logic on failures.""" # Mock to fail twice, then succeed call_count = 0 def mock_response(request): nonlocal call_count call_count += 1 if 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) 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 await client.close() @pytest.mark.asyncio @respx.mock async def test_context_manager(wled_url, mock_wled_info): """Test async context manager usage.""" respx.get(f"{wled_url}/json/info").mock( return_value=Response(200, json=mock_wled_info) ) async with WLEDClient(wled_url) as client: assert client.is_connected is True # Client should be closed after context assert client.is_connected is False @pytest.mark.asyncio async def test_request_without_connection(wled_url): """Test making request without connecting first.""" client = WLEDClient(wled_url) with pytest.raises(RuntimeError): await client.get_state()