Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
613
.dss/test_api_phase3.py
Normal file
613
.dss/test_api_phase3.py
Normal file
@@ -0,0 +1,613 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DSS Admin UI - Phase 3: API Integration Testing
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Framework: Pytest-Playwright + httpx (Python-based)
|
||||
Purpose: Validate all 79+ API endpoints are working correctly
|
||||
Coverage: Endpoint availability, response validation, error handling
|
||||
|
||||
Test Strategy:
|
||||
- Test each of 79+ endpoints
|
||||
- Validate response schemas
|
||||
- Check error handling paths
|
||||
- Confirm CORS/auth configuration
|
||||
|
||||
Generated: 2025-12-08
|
||||
Author: Gemini 3 Pro Expert Analysis
|
||||
Status: Ready for Implementation
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import httpx
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
from datetime import datetime
|
||||
import re
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# API Client Configuration
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
API_BASE_URL = "http://localhost:8002" # FastAPI backend
|
||||
DEV_CLIENT_URL = "http://localhost:5173" # Vite dev client
|
||||
|
||||
|
||||
# API Endpoints grouped by category (from FastAPI /openapi.json)
|
||||
API_ENDPOINTS = {
|
||||
"Authentication": [
|
||||
("POST", "/api/auth/login", {"username": "test", "password": "test"}),
|
||||
("GET", "/api/auth/me", None),
|
||||
("POST", "/api/auth/logout", None),
|
||||
],
|
||||
|
||||
"Projects": [
|
||||
("GET", "/api/projects", None),
|
||||
("POST", "/api/projects", {
|
||||
"name": "test-project",
|
||||
"description": "Test project for validation"
|
||||
}),
|
||||
("GET", "/api/projects/{id}", None),
|
||||
("PUT", "/api/projects/{id}", {
|
||||
"name": "updated-project",
|
||||
"description": "Updated test project"
|
||||
}),
|
||||
("DELETE", "/api/projects/{id}", None),
|
||||
],
|
||||
|
||||
"Browser Logs": [
|
||||
("GET", "/api/logs/browser", None),
|
||||
("POST", "/api/logs/browser", {
|
||||
"level": "info",
|
||||
"message": "Test log entry",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}),
|
||||
("GET", "/api/browser-logs", None),
|
||||
("DELETE", "/api/browser-logs", None),
|
||||
],
|
||||
|
||||
"Design Tokens": [
|
||||
("GET", "/api/tokens", None),
|
||||
("GET", "/api/tokens/current", None),
|
||||
("POST", "/api/tokens", {
|
||||
"name": "test-token",
|
||||
"value": "#FF0000"
|
||||
}),
|
||||
("PUT", "/api/tokens/{id}", {
|
||||
"value": "#00FF00"
|
||||
}),
|
||||
],
|
||||
|
||||
"Figma Integration": [
|
||||
("GET", "/api/figma/status", None),
|
||||
("POST", "/api/figma/extract", {
|
||||
"file_key": "test-file-key"
|
||||
}),
|
||||
("POST", "/api/figma/sync", {
|
||||
"file_key": "test-file-key"
|
||||
}),
|
||||
("GET", "/api/figma/files", None),
|
||||
("POST", "/api/figma/components/extract", {
|
||||
"file_key": "test-file-key"
|
||||
}),
|
||||
("POST", "/api/figma/validate", {
|
||||
"file_key": "test-file-key"
|
||||
}),
|
||||
("GET", "/api/figma/components", None),
|
||||
("POST", "/api/figma/audit", {
|
||||
"file_key": "test-file-key"
|
||||
}),
|
||||
],
|
||||
|
||||
"MCP Tools": [
|
||||
("GET", "/api/mcp/tools", None),
|
||||
("GET", "/api/mcp/tools/{tool_id}", None),
|
||||
("POST", "/api/mcp/tools/{tool_id}/execute", {
|
||||
"params": {}
|
||||
}),
|
||||
("GET", "/api/mcp/resources", None),
|
||||
("POST", "/api/mcp/resources/{resource_id}", None),
|
||||
],
|
||||
|
||||
"System & Admin": [
|
||||
("GET", "/api/system/status", None),
|
||||
("POST", "/api/system/reset", None),
|
||||
("GET", "/api/admin/teams", None),
|
||||
("POST", "/api/admin/teams", {
|
||||
"name": "test-team"
|
||||
}),
|
||||
("GET", "/api/admin/config", None),
|
||||
("PUT", "/api/admin/config", {
|
||||
"key": "test-key",
|
||||
"value": "test-value"
|
||||
}),
|
||||
],
|
||||
|
||||
"Audit & Discovery": [
|
||||
("GET", "/api/audit/logs", None),
|
||||
("GET", "/api/audit/trail", None),
|
||||
("POST", "/api/discovery/ports", None),
|
||||
("GET", "/api/discovery/services", None),
|
||||
],
|
||||
|
||||
"Services": [
|
||||
("GET", "/api/services/storybook", None),
|
||||
("GET", "/api/services/health", None),
|
||||
("POST", "/api/services/restart", None),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Test Fixtures & Utilities
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def http_client():
|
||||
"""Create HTTP client for API testing"""
|
||||
with httpx.Client(base_url=API_BASE_URL, timeout=10.0) as client:
|
||||
yield client
|
||||
|
||||
|
||||
class APIValidator:
|
||||
"""Validate API responses"""
|
||||
|
||||
@staticmethod
|
||||
def is_valid_json_response(response: httpx.Response) -> bool:
|
||||
"""Check if response is valid JSON"""
|
||||
try:
|
||||
response.json()
|
||||
return True
|
||||
except json.JSONDecodeError:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def has_required_cors_headers(response: httpx.Response) -> bool:
|
||||
"""Check if response has CORS headers"""
|
||||
required_headers = [
|
||||
"access-control-allow-origin",
|
||||
"access-control-allow-methods",
|
||||
]
|
||||
return any(
|
||||
header.lower() in [h.lower() for h in response.headers]
|
||||
for header in required_headers
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_error_response(status_code: int) -> bool:
|
||||
"""Check if status code indicates an error"""
|
||||
return status_code >= 400
|
||||
|
||||
@staticmethod
|
||||
def validate_endpoint(
|
||||
method: str,
|
||||
path: str,
|
||||
response: httpx.Response,
|
||||
strict: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Comprehensive endpoint validation
|
||||
|
||||
Returns validation result with status and details
|
||||
"""
|
||||
is_error = APIValidator.is_error_response(response.status_code)
|
||||
is_json = APIValidator.is_valid_json_response(response)
|
||||
has_cors = APIValidator.has_required_cors_headers(response)
|
||||
|
||||
return {
|
||||
"endpoint": f"{method} {path}",
|
||||
"status_code": response.status_code,
|
||||
"success": response.status_code < 500, # Accept 4xx errors (endpoint exists)
|
||||
"is_json": is_json,
|
||||
"has_cors": has_cors,
|
||||
"is_error": is_error,
|
||||
"error_message": response.text if is_error else None,
|
||||
"response_size": len(response.content),
|
||||
"headers": dict(response.headers),
|
||||
}
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Test Classes - Grouped by API Category
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class TestAuthenticationEndpoints:
|
||||
"""Test authentication endpoints"""
|
||||
|
||||
def test_login_endpoint_exists(self, http_client):
|
||||
"""Test: /api/auth/login endpoint exists"""
|
||||
response = http_client.post("/api/auth/login", json={
|
||||
"username": "test",
|
||||
"password": "test"
|
||||
})
|
||||
result = APIValidator.validate_endpoint("POST", "/api/auth/login", response)
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_me_endpoint_exists(self, http_client):
|
||||
"""Test: /api/auth/me endpoint exists"""
|
||||
response = http_client.get("/api/auth/me")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/auth/me", response)
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_auth_returns_json(self, http_client):
|
||||
"""Test: Auth endpoints return JSON"""
|
||||
response = http_client.post("/api/auth/login", json={
|
||||
"username": "test",
|
||||
"password": "test"
|
||||
})
|
||||
assert response.status_code < 500, "Auth endpoint returned 5xx error"
|
||||
|
||||
|
||||
class TestProjectEndpoints:
|
||||
"""Test project management endpoints"""
|
||||
|
||||
def test_list_projects_endpoint(self, http_client):
|
||||
"""Test: GET /api/projects returns project list"""
|
||||
response = http_client.get("/api/projects")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/projects", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
assert result['is_json'], "Response is not valid JSON"
|
||||
|
||||
# Check response structure
|
||||
if result['status_code'] == 200:
|
||||
data = response.json()
|
||||
assert isinstance(data, (list, dict)), "Response should be list or dict"
|
||||
|
||||
def test_create_project_endpoint(self, http_client):
|
||||
"""Test: POST /api/projects creates new project"""
|
||||
response = http_client.post("/api/projects", json={
|
||||
"name": "test-project",
|
||||
"description": "Test project"
|
||||
})
|
||||
result = APIValidator.validate_endpoint("POST", "/api/projects", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
# 201 Created or 200 OK or 400 Bad Request (missing auth) all acceptable
|
||||
assert result['status_code'] in [200, 201, 400, 401, 403], \
|
||||
f"Unexpected status code: {result['status_code']}"
|
||||
|
||||
def test_get_single_project_endpoint(self, http_client):
|
||||
"""Test: GET /api/projects/:id endpoint exists"""
|
||||
response = http_client.get("/api/projects/test-id")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/projects/test-id", response)
|
||||
|
||||
# Endpoint should exist (even if 404 for specific ID)
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestBrowserLogsEndpoints:
|
||||
"""Test browser logging endpoints"""
|
||||
|
||||
def test_get_browser_logs(self, http_client):
|
||||
"""Test: GET /api/logs/browser returns logs"""
|
||||
response = http_client.get("/api/logs/browser")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/logs/browser", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
assert result['is_json'], "Response is not valid JSON"
|
||||
|
||||
def test_post_browser_log(self, http_client):
|
||||
"""Test: POST /api/logs/browser accepts log entry"""
|
||||
response = http_client.post("/api/logs/browser", json={
|
||||
"level": "info",
|
||||
"message": "Test log",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
result = APIValidator.validate_endpoint("POST", "/api/logs/browser", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_browser_logs_route_variant(self, http_client):
|
||||
"""Test: GET /api/browser-logs endpoint (variant)"""
|
||||
response = http_client.get("/api/browser-logs")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/browser-logs", response)
|
||||
|
||||
# Either /api/logs/browser or /api/browser-logs should work
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestFigmaEndpoints:
|
||||
"""Test Figma integration endpoints"""
|
||||
|
||||
def test_figma_status_endpoint(self, http_client):
|
||||
"""Test: GET /api/figma/status endpoint"""
|
||||
response = http_client.get("/api/figma/status")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/figma/status", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_figma_files_endpoint(self, http_client):
|
||||
"""Test: GET /api/figma/files endpoint"""
|
||||
response = http_client.get("/api/figma/files")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/figma/files", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_figma_extract_endpoint(self, http_client):
|
||||
"""Test: POST /api/figma/extract endpoint"""
|
||||
response = http_client.post("/api/figma/extract", json={
|
||||
"file_key": "test-key"
|
||||
})
|
||||
result = APIValidator.validate_endpoint("POST", "/api/figma/extract", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_figma_components_endpoint(self, http_client):
|
||||
"""Test: GET /api/figma/components endpoint"""
|
||||
response = http_client.get("/api/figma/components")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/figma/components", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestMCPToolsEndpoints:
|
||||
"""Test MCP tool integration endpoints"""
|
||||
|
||||
def test_list_mcp_tools(self, http_client):
|
||||
"""Test: GET /api/mcp/tools returns tool list"""
|
||||
response = http_client.get("/api/mcp/tools")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/mcp/tools", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
assert result['is_json'], "Response is not valid JSON"
|
||||
|
||||
def test_list_mcp_resources(self, http_client):
|
||||
"""Test: GET /api/mcp/resources returns resources"""
|
||||
response = http_client.get("/api/mcp/resources")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/mcp/resources", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_execute_mcp_tool(self, http_client):
|
||||
"""Test: POST /api/mcp/tools/:id/execute endpoint exists"""
|
||||
response = http_client.post("/api/mcp/tools/test-tool/execute", json={
|
||||
"params": {}
|
||||
})
|
||||
result = APIValidator.validate_endpoint(
|
||||
"POST", "/api/mcp/tools/test-tool/execute", response
|
||||
)
|
||||
|
||||
# Endpoint should exist (even if 404 for specific tool)
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestSystemAdminEndpoints:
|
||||
"""Test system and admin endpoints"""
|
||||
|
||||
def test_system_status_endpoint(self, http_client):
|
||||
"""Test: GET /api/system/status endpoint"""
|
||||
response = http_client.get("/api/system/status")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/system/status", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_list_teams_endpoint(self, http_client):
|
||||
"""Test: GET /api/admin/teams endpoint"""
|
||||
response = http_client.get("/api/admin/teams")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/admin/teams", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_get_admin_config_endpoint(self, http_client):
|
||||
"""Test: GET /api/admin/config endpoint"""
|
||||
response = http_client.get("/api/admin/config")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/admin/config", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestAuditDiscoveryEndpoints:
|
||||
"""Test audit and discovery endpoints"""
|
||||
|
||||
def test_audit_logs_endpoint(self, http_client):
|
||||
"""Test: GET /api/audit/logs endpoint"""
|
||||
response = http_client.get("/api/audit/logs")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/audit/logs", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_audit_trail_endpoint(self, http_client):
|
||||
"""Test: GET /api/audit/trail endpoint"""
|
||||
response = http_client.get("/api/audit/trail")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/audit/trail", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
def test_discovery_services_endpoint(self, http_client):
|
||||
"""Test: GET /api/discovery/services endpoint"""
|
||||
response = http_client.get("/api/discovery/services")
|
||||
result = APIValidator.validate_endpoint("GET", "/api/discovery/services", response)
|
||||
|
||||
assert result['status_code'] < 500, f"Server error: {result['error_message']}"
|
||||
|
||||
|
||||
class TestCORSConfiguration:
|
||||
"""Test CORS configuration across endpoints"""
|
||||
|
||||
def test_cors_headers_on_projects_endpoint(self, http_client):
|
||||
"""Test: /api/projects has CORS headers"""
|
||||
response = http_client.get("/api/projects")
|
||||
|
||||
# Check for CORS headers
|
||||
has_allow_origin = "access-control-allow-origin" in [
|
||||
h.lower() for h in response.headers
|
||||
]
|
||||
# CORS might not be required if same-origin, but document if present
|
||||
print(f"CORS headers present: {has_allow_origin}")
|
||||
|
||||
def test_cors_headers_on_logs_endpoint(self, http_client):
|
||||
"""Test: /api/logs/browser has CORS headers"""
|
||||
response = http_client.post("/api/logs/browser", json={
|
||||
"level": "info",
|
||||
"message": "test"
|
||||
})
|
||||
|
||||
# Check response
|
||||
assert response.status_code < 500, f"Server error on logs endpoint"
|
||||
|
||||
|
||||
class TestErrorHandling:
|
||||
"""Test error handling and edge cases"""
|
||||
|
||||
def test_404_on_nonexistent_resource(self, http_client):
|
||||
"""Test: Non-existent resources return 404"""
|
||||
response = http_client.get("/api/projects/nonexistent-id")
|
||||
|
||||
# Should return 404, not 500
|
||||
assert response.status_code in [404, 401, 403], \
|
||||
f"Expected 4xx error, got {response.status_code}"
|
||||
|
||||
def test_method_not_allowed(self, http_client):
|
||||
"""Test: Invalid HTTP methods return 405"""
|
||||
response = http_client.request("PATCH", "/api/projects")
|
||||
|
||||
# Should return 405 or 404, not 500
|
||||
assert response.status_code in [405, 404, 500], \
|
||||
f"Response for invalid method: {response.status_code}"
|
||||
|
||||
def test_invalid_json_body(self, http_client):
|
||||
"""Test: Invalid JSON body is handled gracefully"""
|
||||
response = httpx.post(
|
||||
f"{API_BASE_URL}/api/projects",
|
||||
content=b"invalid json {",
|
||||
headers={"Content-Type": "application/json"}
|
||||
)
|
||||
|
||||
# Should return 400, not 500
|
||||
assert response.status_code != 500, "Server error on invalid JSON"
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Comprehensive API Scan Test
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_comprehensive_api_scan(http_client):
|
||||
"""
|
||||
Test: Scan all known API endpoints and report health
|
||||
|
||||
This comprehensive test hits all endpoints and generates a report
|
||||
"""
|
||||
|
||||
results = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"total_endpoints": 0,
|
||||
"successful": 0,
|
||||
"errors": 0,
|
||||
"categories": {}
|
||||
}
|
||||
|
||||
for category, endpoints in API_ENDPOINTS.items():
|
||||
results["categories"][category] = {
|
||||
"endpoints": len(endpoints),
|
||||
"passed": 0,
|
||||
"failed": 0,
|
||||
"details": []
|
||||
}
|
||||
|
||||
for method, path, payload in endpoints:
|
||||
results["total_endpoints"] += 1
|
||||
|
||||
try:
|
||||
if method == "GET":
|
||||
response = http_client.get(path)
|
||||
elif method == "POST":
|
||||
response = http_client.post(path, json=payload or {})
|
||||
elif method == "PUT":
|
||||
response = http_client.put(path, json=payload or {})
|
||||
elif method == "DELETE":
|
||||
response = http_client.delete(path)
|
||||
else:
|
||||
continue
|
||||
|
||||
validation = APIValidator.validate_endpoint(method, path, response)
|
||||
|
||||
if validation['success']:
|
||||
results["successful"] += 1
|
||||
results["categories"][category]["passed"] += 1
|
||||
else:
|
||||
results["errors"] += 1
|
||||
results["categories"][category]["failed"] += 1
|
||||
|
||||
results["categories"][category]["details"].append({
|
||||
"endpoint": f"{method} {path}",
|
||||
"status": validation['status_code'],
|
||||
"success": validation['success'],
|
||||
"json": validation['is_json']
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
results["errors"] += 1
|
||||
results["categories"][category]["failed"] += 1
|
||||
results["categories"][category]["details"].append({
|
||||
"endpoint": f"{method} {path}",
|
||||
"error": str(e)
|
||||
})
|
||||
|
||||
# Print report
|
||||
print("\n" + "="*80)
|
||||
print("API ENDPOINT HEALTH REPORT")
|
||||
print("="*80)
|
||||
print(f"Timestamp: {results['timestamp']}")
|
||||
print(f"Total Endpoints: {results['total_endpoints']}")
|
||||
print(f"Successful: {results['successful']} ({results['successful']*100//results['total_endpoints']}%)")
|
||||
print(f"Errors: {results['errors']}")
|
||||
print("\nBy Category:")
|
||||
for category, cat_results in results["categories"].items():
|
||||
print(f" {category}: {cat_results['passed']}/{cat_results['endpoints']}")
|
||||
|
||||
print("="*80)
|
||||
|
||||
# Assert at least 80% of endpoints are working
|
||||
success_rate = results['successful'] / results['total_endpoints']
|
||||
assert success_rate >= 0.8, \
|
||||
f"API health below 80%: {success_rate*100:.1f}% working endpoints"
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Test Configuration
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def print_test_header():
|
||||
"""Print header with test information"""
|
||||
print("\n" + "="*80)
|
||||
print("DSS Admin UI - Phase 3: API Integration Testing")
|
||||
print("="*80)
|
||||
print(f"API Base URL: {API_BASE_URL}")
|
||||
print(f"Endpoints to test: {sum(len(e) for e in API_ENDPOINTS.values())}")
|
||||
print("="*80)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("""
|
||||
╔═══════════════════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ DSS Admin UI - Phase 3 API Testing ║
|
||||
║ pytest-playwright API Endpoint Validation ║
|
||||
║ ║
|
||||
║ Prerequisites: ║
|
||||
║ $ pip install pytest playwright httpx ║
|
||||
║ $ playwright install ║
|
||||
║ ║
|
||||
║ To run all API tests: ║
|
||||
║ $ pytest .dss/test_api_phase3.py -v ║
|
||||
║ ║
|
||||
║ To run specific category: ║
|
||||
║ $ pytest .dss/test_api_phase3.py::TestProjectEndpoints -v ║
|
||||
║ $ pytest .dss/test_api_phase3.py::TestFigmaEndpoints -v ║
|
||||
║ $ pytest .dss/test_api_phase3.py::TestMCPToolsEndpoints -v ║
|
||||
║ ║
|
||||
║ To run comprehensive scan: ║
|
||||
║ $ pytest .dss/test_api_phase3.py::test_comprehensive_api_scan -v ║
|
||||
║ ║
|
||||
║ To run with detailed output: ║
|
||||
║ $ pytest .dss/test_api_phase3.py -vv --tb=short ║
|
||||
║ ║
|
||||
║ To run in CI/CD environment: ║
|
||||
║ $ HEADLESS=1 pytest .dss/test_api_phase3.py -v --tb=short ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════════════════════╝
|
||||
""")
|
||||
Reference in New Issue
Block a user