""" Comprehensive tests for the DevOps Toolkit API. """ import pytest from fastapi.testclient import TestClient from app.main import app @pytest.fixture def client(): """Test client fixture.""" return TestClient(app) class TestRootEndpoint: """Test cases for the root endpoint.""" def test_root_endpoint(self, client): """Test the root endpoint returns correct information.""" response = client.get("/") assert response.status_code == 200 data = response.json() assert "message" in data assert "version" in data assert "docs" in data assert "redoc" in data assert data["message"] == "DeployMate API" assert data["version"] == "1.0.0" class TestScriptGeneration: """Test cases for script generation endpoint.""" def test_generate_python_script(self, client): """Test generating a Python stack script.""" config = { "stackType": "python", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": False, "sslEmail": "", "runtime": "3.11", "processManager": "systemd", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data assert "description" in data assert "nginx_config_path" in data assert "nginx_url" in data assert "python" in data["description"].lower() assert "nginx" in data["description"].lower() def test_generate_nodejs_script(self, client): """Test generating a Node.js stack script.""" config = { "stackType": "nodejs", "webServer": "nginx", "database": "mongodb", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": True, "sslEmail": "test@example.com", "runtime": "20", "processManager": "pm2", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data assert "nodejs" in data["description"].lower() def test_generate_react_script(self, client): """Test generating a React static site script.""" config = { "stackType": "react", "webServer": "nginx", "database": "none", "domain": "example.com", "ssl": False, "processManager": "none", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data assert "react" in data["description"].lower() def test_generate_script_without_webserver(self, client): """Test generating a script without web server.""" config = { "stackType": "python", "webServer": "none", "database": "none", "runtime": "3.11", "processManager": "none", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data # Should not have nginx config when no web server assert data["nginx_config_path"] is None or data["nginx_config_path"] == "" assert data["nginx_url"] is None or data["nginx_url"] == "" def test_generate_script_validation_error(self, client): """Test script generation with invalid data.""" # Invalid stack type config = { "stackType": "invalid_stack", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 # Validation error def test_generate_script_missing_required_fields(self, client): """Test script generation with missing required fields.""" config = { "webServer": "nginx", "database": "postgresql", # Missing stackType } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 # Validation error class TestNginxGeneration: """Test cases for nginx configuration generation.""" def test_generate_nginx_python(self, client): """Test nginx config generation for Python.""" config = { "stackType": "python", "webServer": "nginx", "database": "", "dbName": "", "dbUsername": "", "dbPassword": "", "domain": "example.com", "ssl": False, "sslEmail": "", "runtime": "", "processManager": "", "security": { "ufw": False, "fail2ban": False, "disableRoot": False, "autoUpdates": False, }, "advanced": { "swap": False, "swapSize": "", "sshKey": "", "backups": False, "monitoring": False, }, } response = client.post("/api/v1/generate-nginx-config", json=config) assert response.status_code == 200 data = response.json() assert "nginx_config_path" in data assert "nginx_url" in data assert "description" in data assert "python" in data["description"].lower() def test_generate_nginx_nodejs(self, client): """Test nginx config generation for Node.js.""" config = { "stackType": "nodejs", "webServer": "nginx", "database": "", "dbName": "", "dbUsername": "", "dbPassword": "", "domain": "example.com", "ssl": True, "sslEmail": "test@example.com", "runtime": "", "processManager": "", "security": { "ufw": False, "fail2ban": False, "disableRoot": False, "autoUpdates": False, }, "advanced": { "swap": False, "swapSize": "", "sshKey": "", "backups": False, "monitoring": False, }, } response = client.post("/api/v1/generate-nginx-config", json=config) assert response.status_code == 200 data = response.json() assert "nginx_config_path" in data assert "nginx_url" in data assert "nodejs" in data["description"].lower() def test_generate_nginx_react(self, client): """Test nginx config generation for React.""" config = { "stackType": "react", "webServer": "nginx", "database": "", "dbName": "", "dbUsername": "", "dbPassword": "", "domain": "example.com", "ssl": False, "sslEmail": "", "runtime": "", "processManager": "", "security": { "ufw": False, "fail2ban": False, "disableRoot": False, "autoUpdates": False, }, "advanced": { "swap": False, "swapSize": "", "sshKey": "", "backups": False, "monitoring": False, }, } response = client.post("/api/v1/generate-nginx-config", json=config) assert response.status_code == 200 data = response.json() assert "nginx_config_path" in data assert "nginx_url" in data def test_generate_nginx_validation_error(self, client): """Test nginx config generation with invalid data.""" config = {"stackType": "invalid_stack", "domain": "example.com"} response = client.post("/api/v1/generate-nginx-config", json=config) assert response.status_code == 422 # Validation error class TestFileDownload: """Test cases for file download endpoint.""" def test_download_script_creates_file(self, client): """Test that script generation creates a downloadable file.""" # First generate a script config = { "stackType": "python", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": False, "runtime": "3.11", "processManager": "systemd", } gen_response = client.post("/api/v1/generate-script", json=config) assert gen_response.status_code == 200 gen_data = gen_response.json() # Check that the response contains file URLs assert "script_url" in gen_data assert "nginx_url" in gen_data assert gen_data["script_url"].startswith("/download/") assert gen_data["nginx_url"].startswith("/download/") # Extract filename from URL script_filename = gen_data["script_url"].split("/")[-1] nginx_filename = gen_data["nginx_url"].split("/")[-1] # Check that files exist (without actually downloading them # due to test client issues) from app.core.config import settings script_path = settings.generated_dir / script_filename nginx_path = settings.generated_dir / nginx_filename assert script_path.exists(), f"Script file {script_filename} was not created" assert nginx_path.exists(), f"Nginx file {nginx_filename} was not created" class TestValidation: """Test cases for input validation.""" def test_invalid_domain_format(self, client): """Test validation of domain format.""" config = { "stackType": "python", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "invalid-domain", # Invalid domain format "ssl": False, "runtime": "3.11", "processManager": "systemd", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 def test_weak_database_password(self, client): """Test validation of database password strength.""" config = { "stackType": "python", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "123", # Too short "domain": "example.com", "ssl": False, "runtime": "3.11", "processManager": "systemd", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 def test_invalid_web_server(self, client): """Test validation of web server type.""" config = { "stackType": "python", "webServer": "invalid_server", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": False, "runtime": "3.11", "processManager": "systemd", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 def test_invalid_database_type(self, client): """Test validation of database type.""" config = { "stackType": "python", "webServer": "nginx", "database": "invalid_db", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": False, "runtime": "3.11", "processManager": "systemd", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 422 class TestEdgeCases: """Test cases for edge cases and error handling.""" def test_generate_script_with_all_features(self, client): """Test script generation with all security and advanced features enabled.""" config = { "stackType": "python", "webServer": "nginx", "database": "postgresql", "dbName": "testdb", "dbUsername": "testuser", "dbPassword": "testpassword123", "domain": "example.com", "ssl": True, "sslEmail": "test@example.com", "runtime": "3.11", "processManager": "systemd", "security": { "ufw": True, "fail2ban": True, "disableRoot": True, "autoUpdates": True, }, "advanced": { "swap": True, "swapSize": "4G", "sshKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ...", "backups": True, "monitoring": True, }, } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data def test_generate_script_minimal_config(self, client): """Test script generation with minimal required configuration.""" config = { "stackType": "python", "webServer": "none", "database": "none", "processManager": "none", } response = client.post("/api/v1/generate-script", json=config) assert response.status_code == 200 data = response.json() assert "script_path" in data assert "script_url" in data