""" Tests for Servers API routes. Tests the following endpoints: - POST /api/servers - GET /api/servers - GET /api/servers/{id} - PUT /api/servers/{id} - DELETE /api/servers/{id} """ import pytest from fastapi.testclient import TestClient # Valid SSH private key for testing servers VALID_SSH_KEY = """-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAJC9AVH1vQFR AAAAAtzc2gtZWQyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQA AAECB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAHHZpXRvaXRvQVJNVjJH AAAAFGZpbGVzeXN0ZW0uY2ZnAAAAAQAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAEAAAAEWNvbW1lbnQ6IHRlc3Qga2V5AQIDBAUHBg==""" class TestCreateServer: """Tests for POST /api/servers endpoint.""" def test_create_server_success(self, client: TestClient): """Test creating a new server successfully.""" # First create an SSH key ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] # Create server response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "test-api-token-123", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 201 data = response.json() assert data["code"] == 0 assert data["message"] == "Server created successfully" assert data["data"]["name"] == "test-server" assert data["data"]["url"] == "https://gitea.example.com" assert data["data"]["ssh_key_id"] == ssh_key_id assert data["data"]["id"] > 0 assert data["data"]["status"] == "untested" assert data["data"]["sync_enabled"] is False assert data["data"]["created_at"] > 0 def test_create_server_with_sync_enabled(self, client: TestClient): """Test creating a server with sync enabled.""" # Create SSH key ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-2", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] # Create server with sync enabled response = client.post( "/api/servers", json={ "name": "sync-server", "url": "https://gitea.example.com", "api_token": "test-api-token", "ssh_key_id": ssh_key_id, "sync_enabled": True, "schedule_cron": "0 */6 * * *" }, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 201 data = response.json() assert data["data"]["sync_enabled"] is True assert data["data"]["schedule_cron"] == "0 */6 * * *" def test_create_server_duplicate_name(self, client: TestClient): """Test creating a server with duplicate name fails.""" # Create SSH key ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-3", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] # Create first server client.post( "/api/servers", json={ "name": "duplicate-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) # Try to create duplicate response = client.post( "/api/servers", json={ "name": "duplicate-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 400 assert "already exists" in response.json()["detail"] def test_create_server_invalid_ssh_key_id(self, client: TestClient): """Test creating a server with non-existent SSH key fails.""" response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": 99999 # Non-existent }, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 400 assert "not found" in response.json()["detail"] def test_create_server_no_auth(self, client: TestClient): """Test creating a server without authentication fails.""" response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": 1 } ) assert response.status_code == 401 class TestListServers: """Tests for GET /api/servers endpoint.""" def test_list_servers_empty(self, client: TestClient): """Test listing servers when none exist.""" response = client.get( "/api/servers", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["data"] == [] assert "0 server" in data["message"] def test_list_servers_with_items(self, client: TestClient): """Test listing servers when some exist.""" # Create SSH key ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-list", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] # Create some servers client.post( "/api/servers", json={ "name": "server-1", "url": "https://gitea1.example.com", "api_token": "token1", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) client.post( "/api/servers", json={ "name": "server-2", "url": "https://gitea2.example.com", "api_token": "token2", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) response = client.get( "/api/servers", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert len(data["data"]) == 2 assert "api_token" not in data["data"][0] # Should not expose token def test_list_servers_no_auth(self, client: TestClient): """Test listing servers without authentication fails.""" response = client.get("/api/servers") assert response.status_code == 401 class TestGetServer: """Tests for GET /api/servers/{id} endpoint.""" def test_get_server_success(self, client: TestClient): """Test getting a specific server successfully.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-get", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "get-test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Get the server response = client.get( f"/api/servers/{server_id}", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["data"]["id"] == server_id assert data["data"]["name"] == "get-test-server" assert "api_token" not in data["data"] def test_get_server_not_found(self, client: TestClient): """Test getting a non-existent server fails.""" response = client.get( "/api/servers/99999", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 404 def test_get_server_no_auth(self, client: TestClient): """Test getting a server without authentication fails.""" response = client.get("/api/servers/1") assert response.status_code == 401 class TestUpdateServer: """Tests for PUT /api/servers/{id} endpoint.""" def test_update_server_name(self, client: TestClient): """Test updating a server's name.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-update", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "old-name", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Update name response = client.put( f"/api/servers/{server_id}", json={"name": "new-name"}, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["data"]["name"] == "new-name" def test_update_server_multiple_fields(self, client: TestClient): """Test updating multiple server fields.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-multi", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Update multiple fields response = client.put( f"/api/servers/{server_id}", json={ "sync_enabled": True, "schedule_cron": "0 */6 * * *", "status": "testing" }, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["data"]["sync_enabled"] is True assert data["data"]["schedule_cron"] == "0 */6 * * *" assert data["data"]["status"] == "testing" def test_update_server_api_token(self, client: TestClient): """Test updating a server's API token.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-token", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "old-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Update API token response = client.put( f"/api/servers/{server_id}", json={"api_token": "new-token"}, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 def test_update_server_not_found(self, client: TestClient): """Test updating a non-existent server fails.""" response = client.put( "/api/servers/99999", json={"name": "new-name"}, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 404 def test_update_server_no_fields(self, client: TestClient): """Test updating a server with no fields fails.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-nofield", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Update with empty body response = client.put( f"/api/servers/{server_id}", json={}, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 400 def test_update_server_no_auth(self, client: TestClient): """Test updating a server without authentication fails.""" response = client.put( "/api/servers/1", json={"name": "new-name"} ) assert response.status_code == 401 class TestDeleteServer: """Tests for DELETE /api/servers/{id} endpoint.""" def test_delete_server_success(self, client: TestClient): """Test deleting a server successfully.""" # Create SSH key and server ssh_response = client.post( "/api/ssh-keys", json={"name": "test-key-del", "private_key": VALID_SSH_KEY}, headers={"Authorization": "Bearer test-token"} ) ssh_key_id = ssh_response.json()["data"]["id"] server_response = client.post( "/api/servers", json={ "name": "delete-test-server", "url": "https://gitea.example.com", "api_token": "test-token", "ssh_key_id": ssh_key_id }, headers={"Authorization": "Bearer test-token"} ) server_id = server_response.json()["data"]["id"] # Delete the server response = client.delete( f"/api/servers/{server_id}", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["message"] == "Server deleted successfully" # Verify it's deleted get_response = client.get( f"/api/servers/{server_id}", headers={"Authorization": "Bearer test-token"} ) assert get_response.status_code == 404 def test_delete_server_not_found(self, client: TestClient): """Test deleting a non-existent server fails.""" response = client.delete( "/api/servers/99999", headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 404 def test_delete_server_no_auth(self, client: TestClient): """Test deleting a server without authentication fails.""" response = client.delete("/api/servers/1") assert response.status_code == 401