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
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", "") |