test_api_status.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. """
  2. Unit tests for status and info API endpoints.
  3. Tests the following endpoints:
  4. - GET /serial_status
  5. - GET /list_serial_ports
  6. - GET /api/settings
  7. - GET /api/table-info
  8. """
  9. import pytest
  10. from unittest.mock import patch, MagicMock, AsyncMock
  11. class TestSerialStatus:
  12. """Tests for /serial_status endpoint."""
  13. @pytest.mark.asyncio
  14. async def test_serial_status_when_connected(self, async_client, mock_state):
  15. """Test serial_status returns connected state and the board URL."""
  16. mock_state.conn = MagicMock()
  17. mock_state.conn.is_connected.return_value = True
  18. with patch("main.state", mock_state), \
  19. patch("main.connection_manager.board_url", return_value="http://192.168.68.160"):
  20. response = await async_client.get("/serial_status")
  21. assert response.status_code == 200
  22. data = response.json()
  23. assert data["connected"] is True
  24. assert data["port"] == "http://192.168.68.160"
  25. @pytest.mark.asyncio
  26. async def test_serial_status_when_disconnected(self, async_client, mock_state):
  27. """Test serial_status still reports the configured board URL when disconnected."""
  28. mock_state.conn = None
  29. with patch("main.state", mock_state), \
  30. patch("main.connection_manager.board_url", return_value="http://192.168.68.160"):
  31. response = await async_client.get("/serial_status")
  32. assert response.status_code == 200
  33. data = response.json()
  34. assert data["connected"] is False
  35. assert data["port"] == "http://192.168.68.160"
  36. @pytest.mark.asyncio
  37. async def test_serial_status_with_disconnected_conn(self, async_client, mock_state):
  38. """Test serial_status when conn exists but is disconnected."""
  39. mock_state.conn = MagicMock()
  40. mock_state.conn.is_connected.return_value = False
  41. mock_state.port = "/dev/ttyUSB0"
  42. mock_state.preferred_port = "/dev/ttyUSB0"
  43. with patch("main.state", mock_state):
  44. response = await async_client.get("/serial_status")
  45. assert response.status_code == 200
  46. data = response.json()
  47. assert data["connected"] is False
  48. class TestListSerialPorts:
  49. """Tests for /list_serial_ports endpoint."""
  50. @pytest.mark.asyncio
  51. async def test_list_serial_ports_returns_list(self, async_client):
  52. """Test list_serial_ports returns a list of available ports."""
  53. mock_ports = ["/dev/ttyUSB0", "/dev/ttyACM0"]
  54. with patch("main.connection_manager.list_board_urls", return_value=mock_ports):
  55. response = await async_client.get("/list_serial_ports")
  56. assert response.status_code == 200
  57. data = response.json()
  58. assert isinstance(data, list)
  59. assert "/dev/ttyUSB0" in data
  60. assert "/dev/ttyACM0" in data
  61. @pytest.mark.asyncio
  62. async def test_list_serial_ports_empty(self, async_client):
  63. """Test list_serial_ports returns empty list when no ports."""
  64. with patch("main.connection_manager.list_board_urls", return_value=[]):
  65. response = await async_client.get("/list_serial_ports")
  66. assert response.status_code == 200
  67. data = response.json()
  68. assert data == []
  69. class TestGetAllSettings:
  70. """Tests for /api/settings endpoint."""
  71. @pytest.mark.asyncio
  72. async def test_get_all_settings_returns_expected_structure(self, async_client, mock_state):
  73. """Test get_all_settings returns the unified settings structure."""
  74. mock_state.app_name = "Test Table"
  75. mock_state.custom_logo = None
  76. mock_state.clear_pattern_speed = 150
  77. mock_state.custom_clear_from_in = None
  78. mock_state.custom_clear_from_out = None
  79. mock_state.scheduled_pause_enabled = False
  80. mock_state.scheduled_pause_control_wled = False
  81. mock_state.scheduled_pause_finish_pattern = False
  82. mock_state.scheduled_pause_timezone = None
  83. mock_state.scheduled_pause_time_slots = []
  84. mock_state.homing = 0
  85. mock_state.homing_user_override = False
  86. mock_state.angular_homing_offset_degrees = 0.0
  87. mock_state.auto_home_enabled = False
  88. mock_state.auto_home_after_patterns = 10
  89. mock_state.led_provider = "none"
  90. mock_state.wled_ip = None
  91. mock_state.dw_led_control_mode = "automated"
  92. mock_state.dw_led_idle_timeout_enabled = False
  93. mock_state.dw_led_idle_timeout_minutes = 30
  94. mock_state.mqtt_enabled = False
  95. mock_state.mqtt_broker = None
  96. mock_state.mqtt_port = 1883
  97. mock_state.mqtt_username = None
  98. mock_state.mqtt_password = None
  99. mock_state.mqtt_client_id = "dune_weaver"
  100. mock_state.mqtt_discovery_prefix = "homeassistant"
  101. mock_state.mqtt_device_id = "dune_weaver_01"
  102. mock_state.mqtt_device_name = "Dune Weaver"
  103. mock_state.timezone = "UTC"
  104. with patch("main.state", mock_state):
  105. response = await async_client.get("/api/settings")
  106. assert response.status_code == 200
  107. data = response.json()
  108. # Check top-level structure (auto_play/connection are gone: auto-play
  109. # lives on the board, serial ports no longer exist)
  110. assert "app" in data
  111. assert "patterns" in data
  112. assert "scheduled_pause" in data
  113. assert "homing" in data
  114. assert "led" in data
  115. assert "mqtt" in data
  116. assert "machine" in data
  117. assert "auto_play" not in data
  118. assert "connection" not in data
  119. # Verify specific values
  120. assert data["app"]["name"] == "Test Table"
  121. assert data["patterns"]["clear_pattern_speed"] == 150
  122. assert data["machine"]["timezone"] == "UTC"
  123. class TestGetTableInfo:
  124. """Tests for /api/table-info endpoint."""
  125. @pytest.mark.asyncio
  126. async def test_get_table_info(self, async_client, mock_state):
  127. """Test get_table_info returns table identification info."""
  128. mock_state.table_id = "table-123"
  129. mock_state.table_name = "Living Room Table"
  130. with patch("main.state", mock_state):
  131. with patch("main.version_manager.get_current_version", return_value="1.0.0"):
  132. response = await async_client.get("/api/table-info")
  133. assert response.status_code == 200
  134. data = response.json()
  135. # API returns "id" and "name", not "table_id" and "table_name"
  136. assert data["id"] == "table-123"
  137. assert data["name"] == "Living Room Table"
  138. assert data["version"] == "1.0.0"
  139. @pytest.mark.asyncio
  140. async def test_get_table_info_not_set(self, async_client, mock_state):
  141. """Test get_table_info when not configured."""
  142. mock_state.table_id = None
  143. mock_state.table_name = None
  144. with patch("main.state", mock_state):
  145. with patch("main.version_manager.get_current_version", return_value="1.0.0"):
  146. response = await async_client.get("/api/table-info")
  147. assert response.status_code == 200
  148. data = response.json()
  149. assert data["id"] is None
  150. assert data["name"] is None