Files
pi-pm3/tests/unit/services/test_wifi_service.py
michael 4f35df1781 Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 13:46:22 -08:00

392 lines
12 KiB
Python

"""Unit tests for WiFiService.
Tests the WiFiService business logic layer, ensuring:
- WiFi status queries
- Network scanning and connection
- Mode switching
- Saved network management
- Error handling and error codes
"""
import pytest
from unittest.mock import AsyncMock, Mock
from app.backend.services.wifi_service import WiFiService
from app.backend.managers.wifi_manager import WiFiMode, WiFiStatus, WiFiNetwork, WiFiInterface
class TestWiFiServiceStatus:
"""Test WiFi status queries."""
@pytest.mark.asyncio
async def test_get_status_success(self, mock_wifi_manager):
"""Test successful WiFi status retrieval."""
# Arrange
test_interface = WiFiInterface(
name="wlan0",
mac="00:11:22:33:44:55",
is_usb=False,
is_up=True,
connected=False,
ssid=None,
ip_address="10.3.141.1"
)
mock_wifi_manager.get_status.return_value = WiFiStatus(
mode=WiFiMode.AP,
interfaces=[test_interface],
current_ssid=None,
current_ip=None,
ap_ssid="dangerous-pi",
ap_ip="10.3.141.1",
supports_dual=False
)
service = WiFiService(mock_wifi_manager)
# Act
result = await service.get_status()
# Assert
assert result.success is True
assert result.data["mode"] == "ap"
assert len(result.data["interfaces"]) == 1
assert result.data["interfaces"][0]["name"] == "wlan0"
assert result.data["access_point"]["ssid"] == "dangerous-pi"
assert result.data["supports_dual"] is False
@pytest.mark.asyncio
async def test_get_status_with_client_connection(self, mock_wifi_manager):
"""Test WiFi status with client connection."""
# Arrange
test_interface = WiFiInterface(
name="wlan0",
mac="00:11:22:33:44:55",
is_usb=False,
is_up=True,
connected=True,
ssid="MyNetwork",
ip_address="192.168.1.100"
)
mock_wifi_manager.get_status.return_value = WiFiStatus(
mode=WiFiMode.CLIENT,
interfaces=[test_interface],
current_ssid="MyNetwork",
current_ip="192.168.1.100",
ap_ssid="dangerous-pi",
ap_ip="10.3.141.1",
supports_dual=False
)
service = WiFiService(mock_wifi_manager)
# Act
result = await service.get_status()
# Assert
assert result.success is True
assert result.data["mode"] == "client"
assert result.data["client"]["ssid"] == "MyNetwork"
assert result.data["client"]["ip"] == "192.168.1.100"
@pytest.mark.asyncio
async def test_get_status_exception(self, mock_wifi_manager):
"""Test WiFi status query with exception."""
# Arrange
mock_wifi_manager.get_status.side_effect = Exception("WiFi error")
service = WiFiService(mock_wifi_manager)
# Act
result = await service.get_status()
# Assert
assert result.success is False
assert result.error.code == "wifi_status_error"
assert "WiFi error" in result.error.details
class TestWiFiServiceNetworkScanning:
"""Test network scanning."""
@pytest.mark.asyncio
async def test_scan_networks_success(self, mock_wifi_manager):
"""Test successful network scan."""
# Arrange
test_networks = [
WiFiNetwork(
ssid="TestNetwork",
bssid="AA:BB:CC:DD:EE:FF",
signal_strength=-45,
frequency=2437,
encrypted=True,
in_use=False
),
WiFiNetwork(
ssid="OpenNetwork",
bssid="11:22:33:44:55:66",
signal_strength=-65,
frequency=2462,
encrypted=False,
in_use=False
)
]
mock_wifi_manager.scan_networks.return_value = test_networks
service = WiFiService(mock_wifi_manager)
# Act
result = await service.scan_networks()
# Assert
assert result.success is True
assert result.data["count"] == 2
assert len(result.data["networks"]) == 2
assert result.data["networks"][0]["ssid"] == "TestNetwork"
assert result.data["networks"][0]["encrypted"] is True
assert result.data["networks"][1]["ssid"] == "OpenNetwork"
assert result.data["networks"][1]["encrypted"] is False
@pytest.mark.asyncio
async def test_scan_networks_with_interface(self, mock_wifi_manager):
"""Test network scan with specific interface."""
# Arrange
mock_wifi_manager.scan_networks.return_value = []
service = WiFiService(mock_wifi_manager)
# Act
result = await service.scan_networks(interface="wlan1")
# Assert
assert result.success is True
mock_wifi_manager.scan_networks.assert_called_once_with("wlan1")
@pytest.mark.asyncio
async def test_scan_networks_exception(self, mock_wifi_manager):
"""Test network scan with exception."""
# Arrange
mock_wifi_manager.scan_networks.side_effect = Exception("Scan failed")
service = WiFiService(mock_wifi_manager)
# Act
result = await service.scan_networks()
# Assert
assert result.success is False
assert result.error.code == "wifi_scan_error"
class TestWiFiServiceConnection:
"""Test network connection management."""
@pytest.mark.asyncio
async def test_connect_success(self, mock_wifi_manager):
"""Test successful network connection."""
# Arrange
mock_wifi_manager.connect_to_network.return_value = True
mock_wifi_manager.get_status.return_value = WiFiStatus(
mode=WiFiMode.CLIENT,
interfaces=[],
current_ssid="TestNetwork",
current_ip="192.168.1.100",
ap_ssid=None,
ap_ip=None,
supports_dual=False
)
service = WiFiService(mock_wifi_manager)
# Act
result = await service.connect(
ssid="TestNetwork",
password="testpass123"
)
# Assert
assert result.success is True
assert "TestNetwork" in result.data["message"]
assert result.data["ssid"] == "TestNetwork"
assert result.data["ip"] == "192.168.1.100"
mock_wifi_manager.connect_to_network.assert_called_once_with(
ssid="TestNetwork",
password="testpass123",
interface=None,
hidden=False
)
@pytest.mark.asyncio
async def test_connect_open_network(self, mock_wifi_manager):
"""Test connection to open network."""
# Arrange
mock_wifi_manager.connect_to_network.return_value = True
mock_wifi_manager.get_status.return_value = WiFiStatus(
mode=WiFiMode.CLIENT,
interfaces=[],
current_ssid="OpenNet",
current_ip="192.168.1.50",
ap_ssid=None,
ap_ip=None,
supports_dual=False
)
service = WiFiService(mock_wifi_manager)
# Act
result = await service.connect(ssid="OpenNet", password=None)
# Assert
assert result.success is True
mock_wifi_manager.connect_to_network.assert_called_once_with(
ssid="OpenNet",
password=None,
interface=None,
hidden=False
)
@pytest.mark.asyncio
async def test_connect_failure(self, mock_wifi_manager):
"""Test failed network connection."""
# Arrange
mock_wifi_manager.connect_to_network.return_value = False
service = WiFiService(mock_wifi_manager)
# Act
result = await service.connect(ssid="BadNetwork", password="wrong")
# Assert
assert result.success is False
assert result.error.code == "connection_failed"
@pytest.mark.asyncio
async def test_disconnect_success(self, mock_wifi_manager):
"""Test successful disconnect."""
# Arrange
mock_wifi_manager.disconnect_from_network.return_value = True
service = WiFiService(mock_wifi_manager)
# Act
result = await service.disconnect()
# Assert
assert result.success is True
assert "Disconnected" in result.data["message"]
@pytest.mark.asyncio
async def test_disconnect_failure(self, mock_wifi_manager):
"""Test failed disconnect."""
# Arrange
mock_wifi_manager.disconnect_from_network.return_value = False
service = WiFiService(mock_wifi_manager)
# Act
result = await service.disconnect()
# Assert
assert result.success is False
assert result.error.code == "disconnect_failed"
class TestWiFiServiceModeSwitch:
"""Test WiFi mode switching."""
@pytest.mark.asyncio
async def test_set_mode_ap(self, mock_wifi_manager):
"""Test switching to AP mode."""
# Arrange
mock_wifi_manager.set_mode.return_value = True
service = WiFiService(mock_wifi_manager)
# Act
result = await service.set_mode("ap")
# Assert
assert result.success is True
assert result.data["mode"] == "ap"
@pytest.mark.asyncio
async def test_set_mode_client(self, mock_wifi_manager):
"""Test switching to client mode."""
# Arrange
mock_wifi_manager.set_mode.return_value = True
service = WiFiService(mock_wifi_manager)
# Act
result = await service.set_mode("client")
# Assert
assert result.success is True
assert result.data["mode"] == "client"
@pytest.mark.asyncio
async def test_set_mode_invalid(self, mock_wifi_manager):
"""Test invalid mode."""
# Arrange
service = WiFiService(mock_wifi_manager)
# Act
result = await service.set_mode("invalid_mode")
# Assert
assert result.success is False
assert result.error.code == "invalid_mode"
@pytest.mark.asyncio
async def test_set_mode_dual_not_supported(self, mock_wifi_manager):
"""Test dual mode without USB adapter."""
# Arrange
mock_wifi_manager.set_mode.side_effect = ValueError("Dual mode requires USB WiFi adapter")
service = WiFiService(mock_wifi_manager)
# Act
result = await service.set_mode("dual")
# Assert
assert result.success is False
assert result.error.code == "mode_not_supported"
class TestWiFiServiceSavedNetworks:
"""Test saved network management."""
@pytest.mark.asyncio
async def test_get_saved_networks_success(self, mock_wifi_manager):
"""Test retrieving saved networks."""
# Arrange
saved = [
{"id": "0", "ssid": "HomeNet", "enabled": True},
{"id": "1", "ssid": "WorkNet", "enabled": False}
]
mock_wifi_manager.get_saved_networks.return_value = saved
service = WiFiService(mock_wifi_manager)
# Act
result = await service.get_saved_networks()
# Assert
assert result.success is True
assert result.data["count"] == 2
assert result.data["networks"][0]["ssid"] == "HomeNet"
@pytest.mark.asyncio
async def test_forget_network_success(self, mock_wifi_manager):
"""Test forgetting a saved network."""
# Arrange
mock_wifi_manager.forget_network.return_value = True
service = WiFiService(mock_wifi_manager)
# Act
result = await service.forget_network("OldNetwork")
# Assert
assert result.success is True
assert "OldNetwork" in result.data["message"]
mock_wifi_manager.forget_network.assert_called_once_with("OldNetwork")
@pytest.mark.asyncio
async def test_forget_network_not_found(self, mock_wifi_manager):
"""Test forgetting non-existent network."""
# Arrange
mock_wifi_manager.forget_network.return_value = False
service = WiFiService(mock_wifi_manager)
# Act
result = await service.forget_network("NonExistent")
# Assert
assert result.success is False
assert result.error.code == "network_not_found"