mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-20 00:38:38 +02:00
Add comprehensive automated testing infrastructure
Backend: - pytest configuration with async support and coverage - Unit tests for notification service (23 tests) - Unit tests for smart plug manager (12 tests) - Unit tests for archive service (16 tests) - Integration tests for API endpoints - Fix: notifications now send immediately (digest is summary only) Frontend: - Vitest configuration with jsdom and coverage - MSW for API mocking - Component tests for Toggle, Button, Card, ConfirmModal (77 tests) - Test utilities with custom render wrapper CI/CD: - GitHub Actions workflow for automated testing - Backend lint, unit tests, integration tests - Frontend lint, type-check, unit tests, build
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
"""Integration tests for Printers API endpoints.
|
||||
|
||||
Tests the full request/response cycle for /api/v1/printers/ endpoints.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from unittest.mock import patch, MagicMock, AsyncMock
|
||||
|
||||
|
||||
class TestPrintersAPI:
|
||||
"""Integration tests for /api/v1/printers/ endpoints."""
|
||||
|
||||
# ========================================================================
|
||||
# List endpoints
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_list_printers_empty(self, async_client: AsyncClient):
|
||||
"""Verify empty list is returned when no printers exist."""
|
||||
response = await async_client.get("/api/v1/printers/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_list_printers_with_data(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify list returns existing printers."""
|
||||
printer = await printer_factory(name="Test Printer")
|
||||
|
||||
response = await async_client.get("/api/v1/printers/")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
assert any(p["name"] == "Test Printer" for p in data)
|
||||
|
||||
# ========================================================================
|
||||
# Create endpoints
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_create_printer(self, async_client: AsyncClient):
|
||||
"""Verify printer can be created."""
|
||||
data = {
|
||||
"name": "New Printer",
|
||||
"serial_number": "00M09A111111111",
|
||||
"ip_address": "192.168.1.100",
|
||||
"access_code": "12345678",
|
||||
"is_active": True,
|
||||
"model": "X1C",
|
||||
}
|
||||
|
||||
response = await async_client.post("/api/v1/printers/", json=data)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["name"] == "New Printer"
|
||||
assert result["serial_number"] == "00M09A111111111"
|
||||
assert result["model"] == "X1C"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_create_printer_duplicate_serial(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify duplicate serial number is rejected."""
|
||||
await printer_factory(serial_number="00M09A222222222")
|
||||
|
||||
data = {
|
||||
"name": "Duplicate Printer",
|
||||
"serial_number": "00M09A222222222",
|
||||
"ip_address": "192.168.1.101",
|
||||
"access_code": "12345678",
|
||||
}
|
||||
|
||||
response = await async_client.post("/api/v1/printers/", json=data)
|
||||
|
||||
# Should fail due to duplicate serial
|
||||
assert response.status_code in [400, 409, 422, 500]
|
||||
|
||||
# ========================================================================
|
||||
# Get single endpoint
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_get_printer(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify single printer can be retrieved."""
|
||||
printer = await printer_factory(name="Get Test Printer")
|
||||
|
||||
response = await async_client.get(f"/api/v1/printers/{printer.id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["id"] == printer.id
|
||||
assert result["name"] == "Get Test Printer"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_get_printer_not_found(self, async_client: AsyncClient):
|
||||
"""Verify 404 for non-existent printer."""
|
||||
response = await async_client.get("/api/v1/printers/9999")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
# ========================================================================
|
||||
# Update endpoints
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_update_printer_name(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify printer name can be updated."""
|
||||
printer = await printer_factory(name="Original Name")
|
||||
|
||||
response = await async_client.patch(
|
||||
f"/api/v1/printers/{printer.id}",
|
||||
json={"name": "Updated Name"}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["name"] == "Updated Name"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_update_printer_active_status(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify printer active status can be updated."""
|
||||
printer = await printer_factory(is_active=True)
|
||||
|
||||
response = await async_client.patch(
|
||||
f"/api/v1/printers/{printer.id}",
|
||||
json={"is_active": False}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["is_active"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_update_printer_auto_archive(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify auto_archive setting can be updated."""
|
||||
printer = await printer_factory(auto_archive=True)
|
||||
|
||||
response = await async_client.patch(
|
||||
f"/api/v1/printers/{printer.id}",
|
||||
json={"auto_archive": False}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["auto_archive"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_update_nonexistent_printer(self, async_client: AsyncClient):
|
||||
"""Verify updating non-existent printer returns 404."""
|
||||
response = await async_client.patch(
|
||||
"/api/v1/printers/9999",
|
||||
json={"name": "New Name"}
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
# ========================================================================
|
||||
# Delete endpoints
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_delete_printer(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify printer can be deleted."""
|
||||
printer = await printer_factory()
|
||||
printer_id = printer.id
|
||||
|
||||
response = await async_client.delete(f"/api/v1/printers/{printer_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify deleted
|
||||
response = await async_client.get(f"/api/v1/printers/{printer_id}")
|
||||
assert response.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_delete_nonexistent_printer(self, async_client: AsyncClient):
|
||||
"""Verify deleting non-existent printer returns 404."""
|
||||
response = await async_client.delete("/api/v1/printers/9999")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
# ========================================================================
|
||||
# Status endpoint
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_get_printer_status(
|
||||
self, async_client: AsyncClient, printer_factory, mock_printer_manager, db_session
|
||||
):
|
||||
"""Verify printer status can be retrieved."""
|
||||
printer = await printer_factory()
|
||||
|
||||
response = await async_client.get(f"/api/v1/printers/{printer.id}/status")
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert "connected" in result
|
||||
assert "state" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_get_printer_status_not_found(self, async_client: AsyncClient):
|
||||
"""Verify 404 for status of non-existent printer."""
|
||||
response = await async_client.get("/api/v1/printers/9999/status")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
# ========================================================================
|
||||
# Test connection endpoint
|
||||
# ========================================================================
|
||||
|
||||
class TestPrinterDataIntegrity:
|
||||
"""Tests for printer data integrity."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_printer_stores_all_fields(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""Verify printer stores all fields correctly."""
|
||||
printer = await printer_factory(
|
||||
name="Full Test Printer",
|
||||
serial_number="00M09A444444444",
|
||||
ip_address="192.168.1.150",
|
||||
model="P1S",
|
||||
is_active=True,
|
||||
auto_archive=False,
|
||||
)
|
||||
|
||||
response = await async_client.get(f"/api/v1/printers/{printer.id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["name"] == "Full Test Printer"
|
||||
assert result["serial_number"] == "00M09A444444444"
|
||||
assert result["ip_address"] == "192.168.1.150"
|
||||
assert result["model"] == "P1S"
|
||||
assert result["is_active"] is True
|
||||
assert result["auto_archive"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_printer_update_persists(
|
||||
self, async_client: AsyncClient, printer_factory, db_session
|
||||
):
|
||||
"""CRITICAL: Verify printer updates persist."""
|
||||
printer = await printer_factory(name="Original", is_active=True)
|
||||
|
||||
# Update
|
||||
await async_client.patch(
|
||||
f"/api/v1/printers/{printer.id}",
|
||||
json={"name": "Updated", "is_active": False}
|
||||
)
|
||||
|
||||
# Verify persistence
|
||||
response = await async_client.get(f"/api/v1/printers/{printer.id}")
|
||||
result = response.json()
|
||||
assert result["name"] == "Updated"
|
||||
assert result["is_active"] is False
|
||||
Reference in New Issue
Block a user