Files

135 lines
4.2 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from typing import Any
import httpx
@dataclass(frozen=True)
class ReleaseTestConfig:
base_url: str
admin_username: str
admin_password: str
timeout_seconds: float
tenant_a_code: str
tenant_a_name: str
tenant_b_code: str
tenant_b_name: str
module_name: str
module_path: str
class ReleaseApiClient:
def __init__(self, config: ReleaseTestConfig, token: str | None = None) -> None:
self.config = config
self.token = token
self._client = httpx.Client(base_url=config.base_url, timeout=config.timeout_seconds)
def close(self) -> None:
self._client.close()
def with_token(self, token: str) -> "ReleaseApiClient":
return ReleaseApiClient(self.config, token=token)
def request(self, method: str, path: str, *, expected_status: int | None = 200, **kwargs: Any) -> httpx.Response:
headers = dict(kwargs.pop("headers", {}) or {})
if self.token:
headers["Authorization"] = f"Bearer {self.token}"
response = self._client.request(method, path, headers=headers, **kwargs)
if expected_status is not None:
assert response.status_code == expected_status, self._format_error(response)
return response
def get(self, path: str, **kwargs: Any) -> httpx.Response:
return self.request("GET", path, **kwargs)
def post(self, path: str, **kwargs: Any) -> httpx.Response:
return self.request("POST", path, **kwargs)
def put(self, path: str, **kwargs: Any) -> httpx.Response:
return self.request("PUT", path, **kwargs)
def patch(self, path: str, **kwargs: Any) -> httpx.Response:
return self.request("PATCH", path, **kwargs)
def delete(self, path: str, **kwargs: Any) -> httpx.Response:
return self.request("DELETE", path, **kwargs)
def login_password(self, username: str, password: str) -> dict[str, Any]:
response = self.post(
"/api/auth/login",
json={"username": username, "password": password},
expected_status=200,
)
payload = response.json()
assert payload.get("success") is True, payload
data = payload.get("data") or {}
assert data.get("access_token"), payload
return data
def login_oauth(
self,
*,
sub: str,
username: str,
nickname: str,
ou_id: str,
ou_name: str,
area: str | None = None,
) -> dict[str, Any]:
response = self.post(
"/api/auth/login",
json={
"userInfo": {
"sub": sub,
"username": username,
"nickname": nickname,
"email": f"{username}@pytest.local",
"phone_number": "13800000000",
"ou_id": ou_id,
"ou_name": ou_name,
"is_leader": False,
},
"area": area,
"expiresIn": 3600,
},
expected_status=200,
)
payload = response.json()
assert payload.get("success") is True, payload
data = payload.get("data") or {}
assert data.get("access_token"), payload
return data
@staticmethod
def json_data(response: httpx.Response) -> Any:
payload = response.json()
if isinstance(payload, dict) and "data" in payload:
return payload["data"]
return payload
@staticmethod
def _format_error(response: httpx.Response) -> str:
try:
body = response.json()
except Exception:
body = response.text
return f"{response.request.method} {response.request.url} -> {response.status_code}: {body}"
def flatten_route_paths(routes: list[dict[str, Any]]) -> set[str]:
paths: set[str] = set()
def collect(items: list[dict[str, Any]]) -> None:
for item in items:
route_path = str(item.get("route_path") or "")
if route_path:
paths.add(route_path)
children = item.get("children") or []
if isinstance(children, list):
collect(children)
collect(routes)
return paths