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
229 lines
8.7 KiB
Python
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
|