chore: reorganize test files into domain-specific subdirectories under tests/
Split the monolithic test directory into three tiers (unit, api, e2e) with a path-mirroring directory structure. Added corresponding Makefile targets (test-unit, test-api, test-e2e) and updated all documentation references (CLAUDE.md, README.md, testing-cicd.html, testing-framework.html, testing-make.html) to reflect the new layout and naming conventions.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import json
|
||||
import requests
|
||||
from starlette.requests import Request
|
||||
from tests.conftest import BASE_URL, run_async
|
||||
from devplacepy.database import get_table, set_setting
|
||||
from devplacepy.utils import generate_uid
|
||||
import devplacepy.services.openai_gateway.gateway as gwmod
|
||||
from devplacepy.services.openai_gateway import GatewayService
|
||||
class FakeResp_openai_gateway:
|
||||
def __init__(self, status=200, payload=None, ctype="application/json", content=b""):
|
||||
self.status_code = status
|
||||
self._payload = payload
|
||||
self.text = json.dumps(payload) if payload is not None else ""
|
||||
self.headers = {"content-type": ctype}
|
||||
self.content = content
|
||||
|
||||
def json(self):
|
||||
if self._payload is None:
|
||||
raise ValueError("no json")
|
||||
return self._payload
|
||||
class FakeRequest:
|
||||
def __init__(self, method, url, json_body):
|
||||
self.method = method
|
||||
self.url = url
|
||||
self.json_body = json_body
|
||||
self.extensions = {}
|
||||
class FakeClient_openai_gateway:
|
||||
def __init__(self, *a, **k):
|
||||
self.calls = []
|
||||
|
||||
def build_request(self, method, url, headers=None, json=None, content=None):
|
||||
return FakeRequest(method, url, json)
|
||||
|
||||
async def send(self, request):
|
||||
self.calls.append((request.url, request.json_body))
|
||||
body = request.json_body or {}
|
||||
return FakeResp_openai_gateway(
|
||||
payload={
|
||||
"id": "x",
|
||||
"model": body.get("model"),
|
||||
"choices": [{"message": {"content": "hi there"}}],
|
||||
}
|
||||
)
|
||||
|
||||
async def post(self, url, headers=None, json=None, timeout=None):
|
||||
self.calls.append((url, json))
|
||||
return FakeResp_openai_gateway(
|
||||
payload={
|
||||
"id": "x",
|
||||
"model": json.get("model"),
|
||||
"choices": [{"message": {"content": "hi there"}}],
|
||||
}
|
||||
)
|
||||
|
||||
async def request(self, method, url, headers=None, content=None):
|
||||
return FakeResp_openai_gateway(payload={"ok": True})
|
||||
|
||||
async def aclose(self):
|
||||
pass
|
||||
def _make_request_openai_gateway(headers=None, cookies=None):
|
||||
headers = headers or {}
|
||||
raw = [(k.lower().encode(), v.encode()) for k, v in headers.items()]
|
||||
if cookies:
|
||||
raw.append(
|
||||
(b"cookie", "; ".join(f"{k}={v}" for k, v in cookies.items()).encode())
|
||||
)
|
||||
return Request(
|
||||
{
|
||||
"type": "http",
|
||||
"method": "POST",
|
||||
"path": "/openai/v1/chat/completions",
|
||||
"query_string": b"",
|
||||
"headers": raw,
|
||||
"state": {},
|
||||
}
|
||||
)
|
||||
def _make_admin_openai_gateway(role="Admin"):
|
||||
username = f"gw_{generate_uid()[:8]}"
|
||||
api_key = generate_uid()
|
||||
get_table("users").insert(
|
||||
{
|
||||
"uid": generate_uid(),
|
||||
"username": username,
|
||||
"email": f"{username}@t.dev",
|
||||
"api_key": api_key,
|
||||
"role": role,
|
||||
"is_active": True,
|
||||
}
|
||||
)
|
||||
return username, api_key
|
||||
def _config(page, **fields):
|
||||
page.request.post(f"{BASE_URL}/admin/services/openai/config", form=fields)
|
||||
|
||||
|
||||
def test_gateway_disabled_returns_503(alice):
|
||||
page, _ = alice
|
||||
page.request.post(f"{BASE_URL}/admin/services/openai/stop")
|
||||
try:
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/openai/v1/chat/completions", json={"messages": []}
|
||||
)
|
||||
assert r.status_code == 503
|
||||
finally:
|
||||
page.request.post(f"{BASE_URL}/admin/services/openai/start")
|
||||
|
||||
|
||||
def test_gateway_auth_and_routing(alice):
|
||||
page, user = alice
|
||||
page.request.post(f"{BASE_URL}/admin/services/openai/start")
|
||||
_config(
|
||||
page,
|
||||
gateway_upstream_url="http://127.0.0.1:9/chat/completions",
|
||||
gateway_vision_enabled="0",
|
||||
gateway_allow_admins="1",
|
||||
gateway_access_key="gwkey",
|
||||
)
|
||||
try:
|
||||
no_creds = requests.post(
|
||||
f"{BASE_URL}/openai/v1/chat/completions", json={"messages": []}
|
||||
)
|
||||
assert no_creds.status_code == 401
|
||||
|
||||
with_key = requests.post(
|
||||
f"{BASE_URL}/openai/v1/chat/completions",
|
||||
headers={"X-API-KEY": "gwkey"},
|
||||
json={"messages": [{"role": "user", "content": "hi"}]},
|
||||
)
|
||||
assert with_key.status_code == 502 # auth passed, upstream unreachable
|
||||
|
||||
admin_key = get_table("users").find_one(username=user["username"])["api_key"]
|
||||
with_admin = requests.post(
|
||||
f"{BASE_URL}/openai/v1/chat/completions",
|
||||
headers={"Authorization": f"Bearer {admin_key}"},
|
||||
json={"messages": [{"role": "user", "content": "hi"}]},
|
||||
)
|
||||
assert with_admin.status_code == 502
|
||||
finally:
|
||||
_config(page, gateway_access_key="")
|
||||
page.request.post(f"{BASE_URL}/admin/services/openai/start")
|
||||
Reference in New Issue
Block a user