-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_api_docs_contract.py
More file actions
110 lines (82 loc) · 4.21 KB
/
Copy pathtest_api_docs_contract.py
File metadata and controls
110 lines (82 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""Contract checks for the generated API documentation surface."""
from __future__ import annotations
import json
from pathlib import Path
import pytest
@pytest.fixture
def docs_client():
pytest.importorskip("flask_swagger_ui", reason="flask_swagger_ui not installed")
from auto_voice.web.app import create_app
app, _ = create_app(
config={
"TESTING": True,
"singing_conversion_enabled": False,
"voice_cloning_enabled": False,
"karaoke_enabled": False,
},
testing=True,
)
return app.test_client()
def test_openapi_json_has_required_top_level_fields(docs_client):
response = docs_client.get("/api/v1/openapi.json")
assert response.status_code == 200
payload = response.get_json()
assert payload["openapi"].startswith("3.")
assert "info" in payload
assert "paths" in payload
def test_openapi_json_documents_training_control_routes(docs_client):
response = docs_client.get("/api/v1/openapi.json")
payload = response.get_json()
paths = payload["paths"]
assert "/api/v1/training/config-options" in paths
assert "/api/v1/training/jobs/{job_id}" in paths
assert "/api/v1/training/jobs/{job_id}/cancel" in paths
assert "/api/v1/training/jobs/{job_id}/pause" in paths
assert "/api/v1/training/jobs/{job_id}/resume" in paths
assert "/api/v1/training/jobs/{job_id}/telemetry" in paths
assert "/api/v1/training/preview/{job_id}" in paths
def test_openapi_json_documents_public_auth_and_rate_limit_controls(docs_client):
response = docs_client.get("/api/v1/openapi.json")
payload = response.get_json()
description = payload["info"]["description"]
paths = payload["paths"]
security_schemes = payload["components"]["securitySchemes"]
assert "AUTOVOICE_API_TOKEN" in description
assert "Authorization: Bearer" in description
assert "RATE_LIMIT" in description
assert "/api/v1/public-commercial/readiness" in paths
assert security_schemes["BearerAuth"]["scheme"] == "bearer"
assert security_schemes["AutoVoiceApiKey"]["name"] == "X-AutoVoice-API-Key"
def test_openapi_yaml_and_swagger_ui_are_exposed(docs_client):
yaml_response = docs_client.get("/api/v1/openapi.yaml")
assert yaml_response.status_code == 200
assert "openapi:" in yaml_response.get_data(as_text=True)
docs_response = docs_client.get("/docs", follow_redirects=True)
assert docs_response.status_code == 200
assert "swagger-ui" in docs_response.get_data(as_text=True).lower()
def test_postman_collection_covers_current_training_control_routes():
collection = json.loads(Path("docs/api/postman_collection.json").read_text())
requests = set()
def walk(items):
for item in items:
request = item.get("request")
if request:
raw_url = request.get("url", {}).get("raw")
requests.add((request.get("method"), raw_url))
for child in item.get("item", []):
walk([child])
walk(collection.get("item", []))
assert ("POST", "{{base_url}}/api/{{api_version}}/training/jobs/{{job_id}}/pause") in requests
assert ("POST", "{{base_url}}/api/{{api_version}}/training/jobs/{{job_id}}/resume") in requests
assert ("GET", "{{base_url}}/api/{{api_version}}/training/jobs/{{job_id}}/telemetry") in requests
assert ("POST", "{{base_url}}/api/{{api_version}}/training/preview/{{job_id}}") in requests
assert ("GET", "{{base_url}}/api/{{api_version}}/voice/profiles/{{profile_id}}/training-status") in requests
assert ("GET", "{{base_url}}/api/{{api_version}}/settings/app") in requests
assert ("PATCH", "{{base_url}}/api/{{api_version}}/settings/app") in requests
def test_secondary_profile_docs_explain_current_route_ownership_and_training_namespace():
profile_doc = Path("docs/api-voice-profile.md").read_text()
architecture_doc = Path("docs/continuous-learning-architecture.md").read_text()
assert "/api/v1/voice/profiles/*" in profile_doc
assert "/api/v1/profiles/*" in profile_doc
assert "There is no separate `/training` Socket.IO namespace" in profile_doc
assert "training currently does not have dedicated room semantics and should be filtered by `job_id`" in architecture_doc