""" 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