homelab_automation/tests/backend/test_routes_bootstrap.py
Bruno Charest ecefbc8611
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Clean up test files and debug artifacts, add node_modules to gitignore, export DashboardManager for testing, and enhance pytest configuration with comprehensive test markers and settings
2025-12-15 08:15:49 -05:00

229 lines
8.7 KiB
Python

"""
Tests pour les routes bootstrap.
"""
import pytest
from unittest.mock import patch, AsyncMock, MagicMock
from httpx import AsyncClient
pytestmark = pytest.mark.unit
class TestGetAllBootstrapStatus:
"""Tests pour GET /api/bootstrap/status."""
@pytest.mark.asyncio
async def test_get_all_status(self, client: AsyncClient):
"""Récupération de tous les statuts."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_service:
mock_service.get_all_status.return_value = {
"host1": {"bootstrap_ok": True},
"host2": {"bootstrap_ok": False}
}
response = await client.get("/api/bootstrap/status")
assert response.status_code == 200
data = response.json()
assert "hosts" in data
@pytest.mark.asyncio
async def test_get_all_status_empty(self, client: AsyncClient):
"""Aucun statut de bootstrap."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_service:
mock_service.get_all_status.return_value = {}
response = await client.get("/api/bootstrap/status")
assert response.status_code == 200
data = response.json()
assert data["hosts"] == {}
class TestGetHostBootstrapStatus:
"""Tests pour GET /api/bootstrap/status/{host_name}."""
@pytest.mark.asyncio
async def test_get_host_status(self, client: AsyncClient):
"""Récupération du statut d'un hôte."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_service:
mock_service.get_bootstrap_status.return_value = {
"bootstrap_ok": True,
"bootstrap_date": "2024-01-01T00:00:00Z"
}
response = await client.get("/api/bootstrap/status/test-host")
assert response.status_code == 200
data = response.json()
assert data["host"] == "test-host"
assert data["bootstrap_ok"] is True
@pytest.mark.asyncio
async def test_get_host_status_not_bootstrapped(self, client: AsyncClient):
"""Hôte non bootstrappé."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_service:
mock_service.get_bootstrap_status.return_value = {
"bootstrap_ok": False
}
response = await client.get("/api/bootstrap/status/new-host")
assert response.status_code == 200
data = response.json()
assert data["bootstrap_ok"] is False
class TestSetHostBootstrapStatus:
"""Tests pour POST /api/bootstrap/status/{host_name}."""
@pytest.mark.asyncio
async def test_set_status_success(self, client: AsyncClient):
"""Définition manuelle du statut."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_bs, \
patch("app.routes.bootstrap.db") as mock_db, \
patch("app.routes.bootstrap.ws_manager") as mock_ws:
mock_bs.set_bootstrap_status.return_value = {
"bootstrap_ok": True,
"bootstrap_date": "2024-01-01T00:00:00Z"
}
mock_db.invalidate_hosts_cache = MagicMock()
mock_ws.broadcast = AsyncMock()
response = await client.post(
"/api/bootstrap/status/test-host?success=true"
)
assert response.status_code == 200
data = response.json()
assert data["host"] == "test-host"
assert data["status"] == "updated"
@pytest.mark.asyncio
async def test_set_status_with_details(self, client: AsyncClient):
"""Définition du statut avec détails."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_bs, \
patch("app.routes.bootstrap.db") as mock_db, \
patch("app.routes.bootstrap.ws_manager") as mock_ws:
mock_bs.set_bootstrap_status.return_value = {"bootstrap_ok": True}
mock_db.invalidate_hosts_cache = MagicMock()
mock_ws.broadcast = AsyncMock()
response = await client.post(
"/api/bootstrap/status/test-host?success=true&details=Manual%20setup"
)
assert response.status_code == 200
mock_bs.set_bootstrap_status.assert_called_once()
@pytest.mark.asyncio
async def test_set_status_broadcasts_ws(self, client: AsyncClient):
"""La mise à jour broadcast un événement WebSocket."""
with patch("app.routes.bootstrap.bootstrap_status_service") as mock_bs, \
patch("app.routes.bootstrap.db") as mock_db, \
patch("app.routes.bootstrap.ws_manager") as mock_ws:
mock_bs.set_bootstrap_status.return_value = {"bootstrap_ok": True}
mock_db.invalidate_hosts_cache = MagicMock()
mock_ws.broadcast = AsyncMock()
await client.post("/api/bootstrap/status/test-host?success=true")
mock_ws.broadcast.assert_called_once()
call_args = mock_ws.broadcast.call_args[0][0]
assert call_args["type"] == "bootstrap_status_updated"
class TestBootstrapHost:
"""Tests pour POST /api/bootstrap."""
@pytest.mark.asyncio
async def test_bootstrap_missing_fields(self, client: AsyncClient):
"""Bootstrap avec champs manquants."""
response = await client.post(
"/api/bootstrap",
json={"host": "test-host"} # Missing root_password
)
assert response.status_code == 422
@pytest.mark.asyncio
async def test_bootstrap_success(self, client: AsyncClient):
"""Bootstrap réussi."""
mock_result = MagicMock()
mock_result.status = "success"
mock_result.return_code = 0
mock_result.stdout = "Bootstrap completed"
mock_result.stderr = ""
with patch("app.routes.bootstrap.bootstrap_host") as mock_bootstrap, \
patch("app.routes.bootstrap.bootstrap_status_service") as mock_bs, \
patch("app.routes.bootstrap.db") as mock_db, \
patch("app.routes.bootstrap.ws_manager") as mock_ws, \
patch("app.routes.bootstrap.notification_service") as mock_notif:
mock_bootstrap.return_value = mock_result
mock_bs.set_bootstrap_status = MagicMock()
mock_db.hosts = []
mock_db.get_next_id = MagicMock(return_value=1)
mock_db.logs = []
mock_db.invalidate_hosts_cache = MagicMock()
mock_ws.broadcast = AsyncMock()
mock_notif.notify_bootstrap_success = AsyncMock()
response = await client.post(
"/api/bootstrap",
json={
"host": "192.168.1.100",
"root_password": "secret",
"automation_user": "ansible"
}
)
assert response.status_code == 200
@pytest.mark.asyncio
async def test_bootstrap_failure(self, client: AsyncClient):
"""Bootstrap échoué."""
mock_result = MagicMock()
mock_result.status = "failed"
mock_result.return_code = 1
mock_result.stdout = ""
mock_result.stderr = "Connection refused"
with patch("app.routes.bootstrap.bootstrap_host") as mock_bootstrap, \
patch("app.routes.bootstrap.notification_service") as mock_notif:
mock_bootstrap.return_value = mock_result
mock_notif.notify_bootstrap_failed = AsyncMock()
response = await client.post(
"/api/bootstrap",
json={
"host": "192.168.1.100",
"root_password": "secret",
"automation_user": "ansible"
}
)
assert response.status_code == 500
@pytest.mark.asyncio
async def test_bootstrap_exception(self, client: AsyncClient):
"""Bootstrap avec exception."""
with patch("app.routes.bootstrap.bootstrap_host") as mock_bootstrap, \
patch("app.routes.bootstrap.db") as mock_db, \
patch("app.routes.bootstrap.notification_service") as mock_notif:
mock_bootstrap.side_effect = Exception("SSH connection failed")
mock_db.get_next_id = MagicMock(return_value=1)
mock_db.logs = []
mock_notif.notify_bootstrap_failed = AsyncMock()
response = await client.post(
"/api/bootstrap",
json={
"host": "192.168.1.100",
"root_password": "secret",
"automation_user": "ansible"
}
)
assert response.status_code == 500