7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
454 lines
16 KiB
Python
454 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Comprehensive test suite that tests ALL API endpoints in the codebase.
|
|
This ensures we have coverage for every single endpoint.
|
|
"""
|
|
|
|
import json
|
|
import time
|
|
|
|
import pytest
|
|
from loguru import logger
|
|
|
|
|
|
class TestAllEndpoints:
|
|
"""Comprehensive API test suite using Flask test client."""
|
|
|
|
def test_all_auth_endpoints(self, authenticated_client):
|
|
"""Test ALL authentication endpoints."""
|
|
logger.info("\n=== Testing ALL Authentication Endpoints ===")
|
|
|
|
# Test endpoints
|
|
endpoints = [
|
|
("GET", "/auth/check", None, 200),
|
|
("GET", "/auth/integrity-check", None, 200),
|
|
]
|
|
|
|
for method, endpoint, data, expected_status in endpoints:
|
|
if method == "GET":
|
|
response = authenticated_client.get(endpoint)
|
|
else:
|
|
response = authenticated_client.post(
|
|
endpoint,
|
|
json=data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code == expected_status, (
|
|
f"{endpoint} failed: {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All Auth endpoints tested")
|
|
|
|
def test_all_settings_endpoints(self, authenticated_client):
|
|
"""Test ALL settings endpoints."""
|
|
logger.info("\n=== Testing ALL Settings Endpoints ===")
|
|
|
|
# Basic GET endpoints
|
|
get_endpoints = [
|
|
"/settings/api",
|
|
"/settings/api/categories",
|
|
"/settings/api/types",
|
|
"/settings/api/ui_elements",
|
|
"/settings/api/available-models",
|
|
"/settings/api/available-search-engines",
|
|
"/settings/api/warnings",
|
|
"/settings/api/ollama-status",
|
|
"/settings/api/rate-limiting/status",
|
|
"/settings/api/data-location",
|
|
]
|
|
|
|
for endpoint in get_endpoints:
|
|
response = authenticated_client.get(endpoint)
|
|
assert response.status_code == 200, (
|
|
f"{endpoint} failed: {response.data}"
|
|
)
|
|
|
|
# Test bulk get
|
|
response = authenticated_client.get(
|
|
"/settings/api/bulk?keys=llm.provider,llm.model"
|
|
)
|
|
assert response.status_code == 200, f"Bulk get failed: {response.data}"
|
|
|
|
# Test setting CRUD (use an allow-listed prefix so the
|
|
# namespace gate permits creation of this throwaway key).
|
|
test_key = "llm.endpoint_test_value"
|
|
test_value = {"value": "test_endpoint_123", "editable": True}
|
|
|
|
# Create/Update
|
|
response = authenticated_client.put(
|
|
f"/settings/api/{test_key}",
|
|
json=test_value,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code in [200, 201], (
|
|
f"Create setting failed: {response.data}"
|
|
)
|
|
|
|
# Get specific setting
|
|
data = json.loads(response.data)
|
|
if isinstance(data, dict) and "setting" in data:
|
|
actual_key = data["setting"].get("key", test_key)
|
|
else:
|
|
actual_key = test_key
|
|
|
|
response = authenticated_client.get(f"/settings/api/{actual_key}")
|
|
assert response.status_code == 200, (
|
|
f"Get setting failed: {response.data}"
|
|
)
|
|
|
|
# Delete
|
|
response = authenticated_client.delete(f"/settings/api/{actual_key}")
|
|
assert response.status_code in [200, 204, 404], (
|
|
f"Delete setting failed: {response.data}"
|
|
)
|
|
|
|
# Test rate limiting reset
|
|
response = authenticated_client.post(
|
|
"/settings/api/rate-limiting/engines/test/reset"
|
|
)
|
|
assert response.status_code in [200, 404], (
|
|
f"Rate limit reset failed: {response.data}"
|
|
)
|
|
|
|
# Test cleanup
|
|
response = authenticated_client.post(
|
|
"/settings/api/rate-limiting/cleanup"
|
|
)
|
|
assert response.status_code == 200, (
|
|
f"Rate limit cleanup failed: {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All Settings endpoints tested")
|
|
|
|
def test_all_research_endpoints(self, authenticated_client):
|
|
"""Test ALL research-related endpoints."""
|
|
# audit: PUNCHLIST reviewed 2026-05 — issue resolved by prior PR (recommendation: REFACTOR_SPLIT).
|
|
logger.info("\n=== Testing ALL Research Endpoints ===")
|
|
|
|
# Set up logging.
|
|
try:
|
|
logger.level("MILESTONE", no=26, color="<magenta><bold>")
|
|
except (TypeError, ValueError):
|
|
pass # Level already registered
|
|
|
|
# Test history endpoint first
|
|
response = authenticated_client.get("/history/api")
|
|
assert response.status_code == 200, (
|
|
f"History API failed: {response.data}"
|
|
)
|
|
|
|
# Start research via main endpoint
|
|
research_data = {
|
|
"query": "Test all endpoints comprehensive",
|
|
"mode": "quick",
|
|
"model_provider": "OLLAMA",
|
|
"model": "llama2",
|
|
"search_engine": "searxng",
|
|
"max_results": 5,
|
|
"time_period": "y",
|
|
"iterations": 1,
|
|
"questions_per_iteration": 2,
|
|
"strategy": "rapid",
|
|
}
|
|
|
|
# Test /api/start_research endpoint
|
|
response = authenticated_client.post(
|
|
"/api/start_research",
|
|
json=research_data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code == 200, (
|
|
f"Start research failed: {response.data}"
|
|
)
|
|
data = json.loads(response.data)
|
|
assert "research_id" in data
|
|
research_id = data["research_id"]
|
|
|
|
# Give it a moment to start
|
|
time.sleep(0.5) # allow: unmarked-sleep
|
|
|
|
# Test various research endpoints
|
|
research_endpoints = [
|
|
("GET", f"/api/research/{research_id}", None, 200),
|
|
("GET", f"/api/research/{research_id}/status", None, 200),
|
|
("GET", f"/research/api/status/{research_id}", None, 200),
|
|
("GET", f"/history/status/{research_id}", None, 200),
|
|
("GET", f"/history/details/{research_id}", None, 200),
|
|
("GET", f"/history/log_count/{research_id}", None, 200),
|
|
]
|
|
|
|
for method, endpoint, req_data, expected_status in research_endpoints:
|
|
if method == "GET":
|
|
response = authenticated_client.get(endpoint)
|
|
else:
|
|
response = authenticated_client.post(
|
|
endpoint,
|
|
json=req_data,
|
|
content_type="application/json",
|
|
)
|
|
# Some endpoints might return 404 if research is still initializing
|
|
assert response.status_code in [expected_status, 404], (
|
|
f"{endpoint} failed: {response.data}"
|
|
)
|
|
|
|
# Test research API routes
|
|
response = authenticated_client.get(
|
|
"/research/api/settings/current-config"
|
|
)
|
|
assert response.status_code == 200, (
|
|
f"Current config failed: {response.data}"
|
|
)
|
|
|
|
# Test Ollama checks
|
|
response = authenticated_client.get("/research/api/check/ollama_status")
|
|
assert response.status_code == 200, (
|
|
f"Ollama status check failed: {response.data}"
|
|
)
|
|
|
|
# Terminate research
|
|
response = authenticated_client.post(f"/api/terminate/{research_id}")
|
|
assert response.status_code in [200, 400, 404], (
|
|
f"Terminate research failed: {response.data}"
|
|
)
|
|
|
|
# Test alternate terminate endpoint
|
|
response = authenticated_client.post(
|
|
f"/research/api/terminate/{research_id}"
|
|
)
|
|
assert response.status_code in [200, 400, 404], (
|
|
f"API terminate research failed: {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All Research endpoints tested")
|
|
|
|
return research_id
|
|
|
|
def test_all_metrics_endpoints(self, authenticated_client):
|
|
"""Test ALL metrics endpoints."""
|
|
logger.info("\n=== Testing ALL Metrics Endpoints ===")
|
|
|
|
# Basic metrics endpoints
|
|
metrics_endpoints = [
|
|
"/metrics/api/metrics",
|
|
"/metrics/api/metrics/enhanced",
|
|
"/metrics/api/pricing",
|
|
"/metrics/api/cost-analytics",
|
|
"/metrics/api/star-reviews",
|
|
"/metrics/api/rate-limiting",
|
|
"/metrics/api/rate-limiting/current",
|
|
]
|
|
|
|
for endpoint in metrics_endpoints:
|
|
response = authenticated_client.get(endpoint)
|
|
assert response.status_code == 200, (
|
|
f"{endpoint} failed: {response.data}"
|
|
)
|
|
|
|
# Test specific model pricing
|
|
response = authenticated_client.get(
|
|
"/metrics/api/pricing/gpt-3.5-turbo"
|
|
)
|
|
assert response.status_code in [200, 404], (
|
|
f"Model pricing failed: {response.data}"
|
|
)
|
|
|
|
# Test cost calculation
|
|
cost_data = {
|
|
"model_name": "gpt-3.5-turbo",
|
|
"prompt_tokens": 1000,
|
|
"completion_tokens": 500,
|
|
}
|
|
response = authenticated_client.post(
|
|
"/metrics/api/cost-calculation",
|
|
json=cost_data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code == 200, (
|
|
f"Cost calculation failed: {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All Metrics endpoints tested")
|
|
|
|
def test_all_benchmark_endpoints(self, authenticated_client):
|
|
"""Test ALL benchmark endpoints."""
|
|
logger.info("\n=== Testing ALL Benchmark Endpoints ===")
|
|
|
|
# Basic benchmark endpoints
|
|
benchmark_endpoints = [
|
|
"/benchmark/api/history",
|
|
"/benchmark/api/configs",
|
|
"/benchmark/api/running",
|
|
"/benchmark/api/search-quality",
|
|
]
|
|
|
|
for endpoint in benchmark_endpoints:
|
|
response = authenticated_client.get(endpoint)
|
|
assert response.status_code == 200, (
|
|
f"{endpoint} failed: {response.data}"
|
|
)
|
|
|
|
# Test config validation
|
|
test_config = {
|
|
"name": "Test Benchmark Config",
|
|
"queries": ["test query 1", "test query 2"],
|
|
"models": ["gpt-3.5-turbo"],
|
|
"search_engines": ["searxng"],
|
|
"iterations": 1,
|
|
}
|
|
|
|
response = authenticated_client.post(
|
|
"/benchmark/api/validate-config",
|
|
json=test_config,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code == 200, (
|
|
f"Validate config failed: {response.data}"
|
|
)
|
|
|
|
# Test simple benchmark start (but don't actually run it)
|
|
simple_benchmark = {
|
|
"query": "test benchmark",
|
|
"models": ["gpt-3.5-turbo"],
|
|
"search_engines": ["searxng"],
|
|
}
|
|
|
|
# Note: We won't actually start a benchmark as it's resource intensive
|
|
# Just verify the endpoint exists
|
|
response = authenticated_client.post(
|
|
"/benchmark/api/start-simple",
|
|
json=simple_benchmark,
|
|
content_type="application/json",
|
|
)
|
|
# Accept 409 (conflict) if another benchmark is running
|
|
assert response.status_code in [200, 409, 400], (
|
|
f"Start simple benchmark status: {response.status_code}, {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All Benchmark endpoints tested")
|
|
|
|
def test_all_history_endpoints(self, authenticated_client):
|
|
"""Test ALL history endpoints."""
|
|
logger.info("\n=== Testing ALL History Endpoints ===")
|
|
|
|
# Basic history endpoint
|
|
response = authenticated_client.get("/history/api")
|
|
assert response.status_code == 200, (
|
|
f"History API failed: {response.data}"
|
|
)
|
|
|
|
logger.info("✅ All History endpoints tested")
|
|
|
|
@pytest.mark.requires_llm
|
|
def test_all_api_v1_endpoints(self, authenticated_client):
|
|
"""Test ALL API v1 endpoints."""
|
|
logger.info("\n=== Testing ALL API v1 Endpoints ===")
|
|
|
|
# Health check (doesn't require auth)
|
|
response = authenticated_client.get("/api/v1/health")
|
|
assert response.status_code == 200, (
|
|
f"Health check failed: {response.data}"
|
|
)
|
|
|
|
# API documentation
|
|
response = authenticated_client.get("/api/v1/")
|
|
assert response.status_code == 200, f"API docs failed: {response.data}"
|
|
|
|
# Quick summary
|
|
summary_data = {"query": "What is machine learning?", "max_tokens": 100}
|
|
response = authenticated_client.post(
|
|
"/api/v1/quick_summary",
|
|
json=summary_data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code in [200, 404, 500], (
|
|
f"Quick summary status: {response.status_code}"
|
|
)
|
|
|
|
# Generate report
|
|
report_data = {
|
|
"query": "Test report generation",
|
|
"research_type": "comprehensive",
|
|
}
|
|
response = authenticated_client.post(
|
|
"/api/v1/generate_report",
|
|
json=report_data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code in [200, 404, 500], (
|
|
f"Generate report status: {response.status_code}"
|
|
)
|
|
|
|
# Analyze documents. The endpoint searches a named local collection;
|
|
# it does not accept an inline "documents" key (that now yields a 400).
|
|
doc_data = {
|
|
"query": "Summarize this document",
|
|
"collection_name": "test_collection",
|
|
}
|
|
response = authenticated_client.post(
|
|
"/api/v1/analyze_documents",
|
|
json=doc_data,
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code in [200, 404, 500], (
|
|
f"Analyze documents status: {response.status_code}"
|
|
)
|
|
|
|
logger.info("✅ All API v1 endpoints tested")
|
|
|
|
def test_miscellaneous_endpoints(self, authenticated_client):
|
|
"""Test miscellaneous endpoints not covered in other categories."""
|
|
logger.info("\n=== Testing Miscellaneous Endpoints ===")
|
|
|
|
# Test static file serving
|
|
response = authenticated_client.get("/static/css/style.css")
|
|
assert response.status_code in [200, 404], "Static file endpoint failed"
|
|
|
|
# Test favicon
|
|
response = authenticated_client.get("/favicon.ico")
|
|
assert response.status_code in [200, 404], "Favicon endpoint failed"
|
|
|
|
# Test pages that should exist
|
|
page_endpoints = [
|
|
"/", # Home page
|
|
"/history", # History page
|
|
"/settings", # Settings page
|
|
"/metrics/", # Metrics dashboard
|
|
"/benchmark/", # Benchmark dashboard
|
|
]
|
|
|
|
for endpoint in page_endpoints:
|
|
response = authenticated_client.get(endpoint)
|
|
assert response.status_code == 200, (
|
|
f"Page {endpoint} failed with status {response.status_code}"
|
|
)
|
|
|
|
logger.info("✅ All miscellaneous endpoints tested")
|
|
|
|
def test_error_handling(self, authenticated_client):
|
|
"""Test error handling for invalid endpoints and methods."""
|
|
logger.info("\n=== Testing Error Handling ===")
|
|
|
|
# Test non-existent endpoint
|
|
response = authenticated_client.get("/api/does-not-exist")
|
|
assert response.status_code == 404, (
|
|
f"Non-existent endpoint should return 404, got {response.status_code}"
|
|
)
|
|
|
|
# Test wrong method
|
|
response = authenticated_client.delete("/auth/check")
|
|
assert response.status_code in [404, 405], (
|
|
f"Wrong method should return 404/405, got {response.status_code}"
|
|
)
|
|
|
|
# Test invalid JSON
|
|
response = authenticated_client.post(
|
|
"/api/start_research",
|
|
data="invalid json",
|
|
content_type="application/json",
|
|
)
|
|
assert response.status_code in [400, 500], (
|
|
f"Invalid JSON should return 400/500, got {response.status_code}"
|
|
)
|
|
|
|
logger.info("✅ Error handling tested")
|