-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconftest.py
More file actions
162 lines (133 loc) · 5.59 KB
/
Copy pathconftest.py
File metadata and controls
162 lines (133 loc) · 5.59 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
Docsie Partner API — Test Suite Configuration
Shared fixtures for all test modules. Loads .env, provides an authenticated
API client, and manages test resource lifecycle (create once, clean up after).
"""
import os
import time
import pytest
import requests
from dotenv import load_dotenv
load_dotenv()
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
BASE_URL = os.environ.get("DOCSIE_BASE_URL", "https://staging.docsie.io").rstrip("/")
API_KEY = os.environ.get("DOCSIE_API_KEY", "")
API_PREFIX = f"{BASE_URL}/api_v2/003"
TEST_VIDEO_URL = os.environ.get(
"TEST_VIDEO_URL",
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
)
TEST_WORKSPACE_ID = os.environ.get("TEST_WORKSPACE_ID", "")
# ---------------------------------------------------------------------------
# API Client
# ---------------------------------------------------------------------------
class DocsieClient:
"""Thin wrapper around requests that handles auth and base URL."""
def __init__(self, base: str, api_key: str):
self.base = base
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Api-Key {api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
})
# Convenience methods with automatic 429 retry --------------------------
def _request(self, method, path, **kw):
"""Send a request with automatic retry on 429 (rate limited)."""
for attempt in range(4):
resp = getattr(self.session, method)(f"{self.base}{path}", **kw)
if resp.status_code != 429:
return resp
retry_after = int(resp.headers.get("Retry-After", 10))
time.sleep(min(retry_after + 1, 30))
return resp # return last 429 if all retries exhausted
def get(self, path, **kw):
return self._request("get", path, **kw)
def post(self, path, **kw):
return self._request("post", path, **kw)
def put(self, path, **kw):
return self._request("put", path, **kw)
def patch(self, path, **kw):
return self._request("patch", path, **kw)
def delete(self, path, **kw):
return self._request("delete", path, **kw)
# Polling helper -------------------------------------------------------
def poll_job(self, job_id, *, endpoint="jobs", timeout=300, interval=10):
"""Poll a job until it reaches a terminal state or times out."""
deadline = time.time() + timeout
while time.time() < deadline:
resp = self.get(f"/{endpoint}/{job_id}/")
data = resp.json()
status = data.get("status") or data.get("job_status", "")
if status in ("done", "failed", "canceled"):
return data
time.sleep(interval)
raise TimeoutError(f"Job {job_id} did not complete within {timeout}s")
def poll_video_job(self, job_id, **kw):
"""Poll a video-to-docs job via its dedicated status endpoint."""
kw.setdefault("timeout", 600)
kw.setdefault("interval", 15)
deadline = time.time() + kw["timeout"]
while time.time() < deadline:
resp = self.get(f"/video-to-docs/{job_id}/status/")
data = resp.json()
st = data.get("status", "")
if st in ("done", "failed", "canceled"):
return data
time.sleep(kw["interval"])
raise TimeoutError(f"Video job {job_id} did not complete within {kw['timeout']}s")
@pytest.fixture(scope="session")
def api():
"""Session-scoped authenticated API client."""
if not API_KEY:
pytest.skip("DOCSIE_API_KEY not set — cannot run API tests")
return DocsieClient(API_PREFIX, API_KEY)
# ---------------------------------------------------------------------------
# Shared test resources — created once per session, cleaned up at the end
# ---------------------------------------------------------------------------
class TestResources:
"""Accumulates IDs of resources created during the test run."""
def __init__(self):
self.workspace_id = os.environ.get("TEST_WORKSPACE_ID", "")
self.documentation_id = os.environ.get("TEST_DOCUMENTATION_ID", "")
self.book_id = os.environ.get("TEST_BOOK_ID", "")
self.version_id = ""
self.language_id = ""
self.article_id = ""
self.deployment_id = ""
self.snippet_id = ""
self.file_id = ""
self.video_job_id = ""
self.generate_job_id = ""
# Track IDs for cleanup
self._created_ids: dict[str, list[str]] = {
"workspaces": [],
"documentation": [],
"books": [],
"versions": [],
"languages": [],
"articles": [],
"deployments": [],
"snippets": [],
}
def track(self, resource_type: str, resource_id: str):
if resource_type in self._created_ids:
self._created_ids[resource_type].append(resource_id)
@pytest.fixture(scope="session")
def resources():
return TestResources()
@pytest.fixture(scope="session")
def test_workspace_id(api):
"""Resolve the test workspace ID — from env or auto-detect the first workspace."""
if TEST_WORKSPACE_ID:
return TEST_WORKSPACE_ID
resp = api.get("/workspaces/?limit=1")
results = resp.json().get("results", [])
if results:
return results[0]["id"]
pytest.skip("No workspaces found for this API key")
@pytest.fixture(scope="session")
def video_url():
return TEST_VIDEO_URL