Initial commit - Phase 3/4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael
2026-01-06 13:45:29 -08:00
parent 1da6730735
commit 4f35df1781
323 changed files with 98287 additions and 1195 deletions

View File

@@ -0,0 +1,305 @@
"""Unit tests for PM3Service.
Tests the PM3Service business logic layer, ensuring:
- Proper session validation
- Command execution flow
- Error handling and error codes
- Response format consistency
"""
import pytest
from unittest.mock import AsyncMock, Mock
from dataclasses import dataclass
from app.backend.services.pm3_service import PM3Service, PM3ServiceResult, PM3ServiceError
@dataclass
class MockPM3Result:
"""Mock PM3Worker command result."""
success: bool
output: str = ""
error: str = None
class TestPM3ServiceCommandExecution:
"""Test command execution with session validation."""
@pytest.mark.asyncio
async def test_execute_command_success(self, mock_pm3_worker, mock_session_manager):
"""Test successful command execution."""
# Arrange
mock_pm3_worker.execute_command.return_value = MockPM3Result(
success=True,
output="Proxmark3 RFID instrument"
)
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.execute_command(
command="hw version",
session_id="test-session"
)
# Assert
assert result.success is True
assert result.data is not None
assert result.data["output"] == "Proxmark3 RFID instrument"
assert result.data["command"] == "hw version"
assert result.data["session_id"] == "test-session"
assert result.error is None
# Verify session was updated
mock_session_manager.update_activity.assert_called_once_with("test-session")
@pytest.mark.asyncio
async def test_execute_command_session_locked(self, mock_pm3_worker, mock_session_manager):
"""Test command execution when another session is active."""
# Arrange
mock_session_manager.can_execute.return_value = False
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.execute_command(
command="hw version",
session_id="test-session"
)
# Assert
assert result.success is False
assert result.error is not None
assert result.error.code == "session_locked"
assert "Another session is active" in result.error.message
assert result.data is None
# Verify command was not executed
mock_pm3_worker.execute_command.assert_not_called()
@pytest.mark.asyncio
async def test_execute_command_pm3_not_connected(self, mock_pm3_worker, mock_session_manager):
"""Test command execution when PM3 is not connected."""
# Arrange
mock_pm3_worker.is_connected.return_value = False
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.execute_command(
command="hw version",
session_id="test-session"
)
# Assert
assert result.success is False
assert result.error is not None
assert result.error.code == "pm3_not_connected"
assert "not connected" in result.error.message.lower()
assert result.data is None
# Verify command was not executed
mock_pm3_worker.execute_command.assert_not_called()
@pytest.mark.asyncio
async def test_execute_command_failure(self, mock_pm3_worker, mock_session_manager):
"""Test command execution when PM3 command fails."""
# Arrange
mock_pm3_worker.execute_command.return_value = MockPM3Result(
success=False,
error="Invalid command"
)
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.execute_command(
command="invalid",
session_id="test-session"
)
# Assert
assert result.success is False
assert result.error is not None
assert result.error.code == "command_failed"
assert "Invalid command" in result.error.details
@pytest.mark.asyncio
async def test_execute_command_exception_handling(self, mock_pm3_worker, mock_session_manager):
"""Test command execution when an exception occurs."""
# Arrange
mock_pm3_worker.execute_command.side_effect = Exception("Connection lost")
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.execute_command(
command="hw version",
session_id="test-session"
)
# Assert
assert result.success is False
assert result.error is not None
assert result.error.code == "execution_error"
assert "Connection lost" in result.error.details
class TestPM3ServiceStatus:
"""Test status query operations."""
@pytest.mark.asyncio
async def test_get_status_connected(self, mock_pm3_worker, mock_session_manager):
"""Test status query when PM3 is connected."""
# Arrange
mock_pm3_worker.execute_command.return_value = MockPM3Result(
success=True,
output="Proxmark3 RFID instrument\nFirmware version: v4.0"
)
mock_session_manager.has_active_session.return_value = True
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.get_status()
# Assert
assert result.success is True
assert result.data["connected"] is True
assert result.data["device_path"] == "/dev/ttyACM0"
assert "Proxmark3" in result.data["version"]
assert result.data["session_active"] is True
@pytest.mark.asyncio
async def test_get_status_not_connected(self, mock_pm3_worker, mock_session_manager):
"""Test status query when PM3 is not connected."""
# Arrange
mock_pm3_worker.is_connected.return_value = False
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.get_status()
# Assert
assert result.success is True
assert result.data["connected"] is False
assert result.data["version"] is None
class TestPM3ServiceConnection:
"""Test connection management."""
@pytest.mark.asyncio
async def test_connect_success(self, mock_pm3_worker, mock_session_manager):
"""Test successful PM3 connection."""
# Arrange
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.connect()
# Assert
assert result.success is True
assert "Connected" in result.data["message"]
mock_pm3_worker.connect.assert_called_once()
@pytest.mark.asyncio
async def test_connect_failure(self, mock_pm3_worker, mock_session_manager):
"""Test PM3 connection failure."""
# Arrange
mock_pm3_worker.connect.side_effect = Exception("Device not found")
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.connect()
# Assert
assert result.success is False
assert result.error.code == "connection_error"
assert "Device not found" in result.error.details
@pytest.mark.asyncio
async def test_disconnect_success(self, mock_pm3_worker, mock_session_manager):
"""Test successful PM3 disconnection."""
# Arrange
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = await service.disconnect()
# Assert
assert result.success is True
assert "Disconnected" in result.data["message"]
mock_pm3_worker.disconnect.assert_called_once()
class TestPM3ServiceSessionManagement:
"""Test session management operations."""
def test_create_session_success(self, mock_pm3_worker, mock_session_manager):
"""Test successful session creation."""
# Arrange
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = service.create_session(force_takeover=False)
# Assert
assert result.success is True
assert result.data["session_id"] == "test-session-id"
mock_session_manager.create_session.assert_called_once_with(force_takeover=False)
def test_create_session_exists(self, mock_pm3_worker, mock_session_manager):
"""Test session creation when session already exists."""
# Arrange
mock_session_manager.create_session.return_value = None
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = service.create_session(force_takeover=False)
# Assert
assert result.success is False
assert result.error.code == "session_exists"
assert "force_takeover" in result.error.details.lower()
def test_release_session(self, mock_pm3_worker, mock_session_manager):
"""Test session release."""
# Arrange
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = service.release_session("test-session")
# Assert
assert result.success is True
assert "released" in result.data["message"].lower()
mock_session_manager.release_session.assert_called_once_with("test-session")
def test_get_session_info_found(self, mock_pm3_worker, mock_session_manager):
"""Test getting session info for existing session."""
# Arrange
from datetime import datetime
mock_session = Mock()
mock_session.session_id = "test-session"
mock_session.created_at = datetime(2025, 1, 1, 12, 0, 0)
mock_session.last_activity = datetime(2025, 1, 1, 12, 5, 0)
mock_session_manager.get_session.return_value = mock_session
mock_session_manager.is_session_active.return_value = True
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = service.get_session_info("test-session")
# Assert
assert result.success is True
assert result.data["session_id"] == "test-session"
assert result.data["is_active"] is True
def test_get_session_info_not_found(self, mock_pm3_worker, mock_session_manager):
"""Test getting session info for non-existent session."""
# Arrange
mock_session_manager.get_session.return_value = None
service = PM3Service(mock_pm3_worker, mock_session_manager)
# Act
result = service.get_session_info("nonexistent")
# Assert
assert result.success is False
assert result.error.code == "session_not_found"