Files
pi-pm3/tests/integration/api/test_pm3_api.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

200 lines
6.7 KiB
Python

"""Integration tests for PM3 API endpoints.
Tests the refactored PM3 API endpoints to ensure:
- Proper integration with PM3Service
- Correct HTTP status codes for different error types
- Request/response format consistency
- Session management integration
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, Mock
from app.backend.services.pm3_service import PM3ServiceResult, PM3ServiceError
class TestPM3APIStatus:
"""Test /api/pm3/status endpoint."""
def test_get_status_success(self, test_client, mock_pm3_service):
"""Test successful status retrieval."""
# Arrange
mock_pm3_service.get_status.return_value = PM3ServiceResult(
success=True,
data={
"connected": True,
"device_path": "/dev/ttyACM0",
"version": "Proxmark3 v4.0",
"session_active": False
}
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.get("/api/pm3/status")
# Assert
assert response.status_code == 200
data = response.json()
assert data["connected"] is True
assert data["device"] == "/dev/ttyACM0"
assert "Proxmark3" in data["version"]
def test_get_status_error(self, test_client, mock_pm3_service):
"""Test status retrieval with error."""
# Arrange
mock_pm3_service.get_status.return_value = PM3ServiceResult(
success=False,
error=PM3ServiceError(
code="status_error",
message="Failed to query status"
)
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.get("/api/pm3/status")
# Assert
assert response.status_code == 500
assert "Failed to query status" in response.json()["detail"]
class TestPM3APICommand:
"""Test /api/pm3/command endpoint."""
def test_execute_command_success(self, test_client, mock_pm3_service):
"""Test successful command execution."""
# Arrange
mock_pm3_service.execute_command.return_value = PM3ServiceResult(
success=True,
data={
"output": "Proxmark3 RFID instrument",
"command": "hw version",
"session_id": "test-session"
}
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post(
"/api/pm3/command",
json={"command": "hw version", "session_id": "test-session"}
)
# Assert
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "Proxmark3" in data["output"]
assert data["error"] is None
def test_execute_command_session_locked(self, test_client, mock_pm3_service):
"""Test command execution with session locked."""
# Arrange
mock_pm3_service.execute_command.return_value = PM3ServiceResult(
success=False,
error=PM3ServiceError(
code="session_locked",
message="Another session is active"
)
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post(
"/api/pm3/command",
json={"command": "hw version", "session_id": "test-session"}
)
# Assert
assert response.status_code == 423 # Locked
assert "Another session is active" in response.json()["detail"]
def test_execute_command_pm3_not_connected(self, test_client, mock_pm3_service):
"""Test command execution when PM3 not connected."""
# Arrange
mock_pm3_service.execute_command.return_value = PM3ServiceResult(
success=False,
error=PM3ServiceError(
code="pm3_not_connected",
message="Proxmark3 not connected"
)
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post(
"/api/pm3/command",
json={"command": "hw version"}
)
# Assert
assert response.status_code == 200 # Returns in body, not HTTP error
data = response.json()
assert data["success"] is False
assert "not connected" in data["error"].lower()
class TestPM3APIConnection:
"""Test PM3 connection endpoints."""
def test_connect_success(self, test_client, mock_pm3_service):
"""Test successful PM3 connection."""
# Arrange
mock_pm3_service.connect.return_value = PM3ServiceResult(
success=True,
data={"message": "Connected to Proxmark3"}
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post("/api/pm3/connect")
# Assert
assert response.status_code == 200
assert response.json()["success"] is True
def test_connect_failure(self, test_client, mock_pm3_service):
"""Test PM3 connection failure."""
# Arrange
mock_pm3_service.connect.return_value = PM3ServiceResult(
success=False,
error=PM3ServiceError(
code="connection_error",
message="Device not found"
)
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post("/api/pm3/connect")
# Assert
assert response.status_code == 503
assert "Device not found" in response.json()["detail"]
def test_disconnect_success(self, test_client, mock_pm3_service):
"""Test successful PM3 disconnection."""
# Arrange
mock_pm3_service.disconnect.return_value = PM3ServiceResult(
success=True,
data={"message": "Disconnected from Proxmark3"}
)
# Act
with patch('app.backend.api.pm3.container') as mock_container:
mock_container.pm3_service = mock_pm3_service
response = test_client.post("/api/pm3/disconnect")
# Assert
assert response.status_code == 200
assert response.json()["success"] is True