You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.8 KiB
Python

"""
Integration tests for complete API workflows
"""
import pytest
from fastapi.testclient import TestClient
from datetime import datetime, timedelta
def test_complete_api_workflow(client: TestClient):
"""Test complete API workflow from health check to data retrieval"""
# 1. Health check
health_response = client.get("/api/v1/health")
assert health_response.status_code == 200
# 2. Get data catalog
catalog_response = client.get("/api/v1/metadata/catalog")
assert catalog_response.status_code == 200
catalog_data = catalog_response.json()
assert "categories" in catalog_data
# 3. Try to get financial data
financial_request = {
"ticker": "AAPL",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-03-31T23:59:59",
"period_type": "quarterly",
"include_metrics": True
}
financial_response = client.post("/api/v1/financial/data", json=financial_request)
# Should succeed or return specific error (not validation error)
assert financial_response.status_code in [200, 404, 500]
# 4. Try OHLCV endpoint (should be not implemented)
ohlcv_request = {
"ticker": "AAPL",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-03-31T23:59:59",
"interval": "1d"
}
ohlcv_response = client.post("/api/v1/market/ohlcv", json=ohlcv_request)
assert ohlcv_response.status_code in [200, 404, 501]
def test_api_error_handling_consistency(client: TestClient):
"""Test that error responses are consistent across endpoints"""
# Test validation errors
validation_errors = []
# Financial endpoint validation error
response = client.post("/api/v1/financial/data", json={"ticker": ""})
if response.status_code == 422:
validation_errors.append(response.json())
# OHLCV endpoint validation error
response = client.post("/api/v1/market/ohlcv", json={"ticker": ""})
if response.status_code == 422:
validation_errors.append(response.json())
# Check validation errors have consistent structure
for error in validation_errors:
assert "detail" in error
# FastAPI validation errors have specific structure
if isinstance(error["detail"], list):
for item in error["detail"]:
assert "type" in item
assert "msg" in item
def test_date_range_validation_consistency(client: TestClient):
"""Test that date range validation is consistent across endpoints"""
# Invalid date range (end before start)
invalid_dates = {
"start_date": "2023-12-31T00:00:00",
"end_date": "2023-01-01T00:00:00"
}
# Test financial endpoint
financial_request = {
"ticker": "AAPL",
**invalid_dates,
"period_type": "quarterly"
}
response = client.post("/api/v1/financial/data", json=financial_request)
assert response.status_code == 400
# Test OHLCV endpoint
ohlcv_request = {
"ticker": "AAPL",
**invalid_dates,
"interval": "1d"
}
response = client.post("/api/v1/market/ohlcv", json=ohlcv_request)
assert response.status_code == 400
def test_ticker_validation_consistency(client: TestClient):
"""Test that ticker validation is consistent across endpoints"""
valid_date_range = {
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-03-31T23:59:59"
}
# Test empty ticker
financial_request = {
"ticker": "",
**valid_date_range,
"period_type": "quarterly"
}
response = client.post("/api/v1/financial/data", json=financial_request)
assert response.status_code == 422
ohlcv_request = {
"ticker": "",
**valid_date_range,
"interval": "1d"
}
response = client.post("/api/v1/market/ohlcv", json=ohlcv_request)
assert response.status_code == 422
def test_response_structure_consistency(client: TestClient):
"""Test that successful responses have consistent structure"""
# Get catalog (should always work)
catalog_response = client.get("/api/v1/metadata/catalog")
assert catalog_response.status_code == 200
catalog_data = catalog_response.json()
# Check common response patterns
assert isinstance(catalog_data, dict)
assert "last_updated" in catalog_data
# Health check
health_response = client.get("/api/v1/health")
assert health_response.status_code == 200
health_data = health_response.json()
assert isinstance(health_data, dict)
assert "timestamp" in health_data
assert "status" in health_data
def test_content_type_headers(client: TestClient):
"""Test that all endpoints return proper content-type headers"""
endpoints = [
"/api/v1/health",
"/api/v1/metadata/catalog"
]
for endpoint in endpoints:
response = client.get(endpoint)
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
def test_cors_and_security_headers(client: TestClient):
"""Test CORS and basic security considerations"""
# Test that endpoints don't expose sensitive information in headers
response = client.get("/api/v1/health")
assert response.status_code == 200
# Should not expose server information
assert "server" not in response.headers or "FastAPI" not in response.headers.get("server", "")
def test_openapi_documentation(client: TestClient):
"""Test that OpenAPI documentation is accessible"""
# OpenAPI JSON should be accessible
response = client.get("/api/v1/openapi.json")
assert response.status_code == 200
openapi_spec = response.json()
assert "openapi" in openapi_spec
assert "info" in openapi_spec
assert "paths" in openapi_spec
# Check that main endpoints are documented
paths = openapi_spec["paths"]
assert "/api/v1/health" in paths
assert "/api/v1/financial/data" in paths
assert "/api/v1/metadata/catalog" in paths
def test_rate_limiting_headers(client: TestClient):
"""Test for rate limiting indicators (if implemented)"""
response = client.get("/api/v1/health")
assert response.status_code == 200
# Rate limiting headers are optional but good to check
# X-RateLimit-* headers would be present if rate limiting is implemented
# This test just ensures we don't break if they're added later
def test_api_versioning(client: TestClient):
"""Test API versioning is properly implemented"""
# All endpoints should be under /api/v1
response = client.get("/api/v1/health")
assert response.status_code == 200
# Root should redirect to docs
response = client.get("/", follow_redirects=False)
assert response.status_code == 307 # Redirect
assert "api/v1" in response.headers.get("location", "")