| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- import json
- from urllib.error import HTTPError
- import pytest
- from ops.sub2api_client import Sub2ApiClient
- class FakeResponse:
- def __init__(self, payload: dict, status: int = 200):
- self.payload = payload
- self.status = status
- def read(self) -> bytes:
- return json.dumps(self.payload).encode("utf-8")
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc, tb):
- return False
- class FakeHttpError(HTTPError):
- def __init__(self, url: str, code: int, payload: dict):
- super().__init__(url, code, "error", hdrs=None, fp=None)
- self._payload = payload
- def read(self) -> bytes:
- return json.dumps(self._payload).encode("utf-8")
- def _decode_body(request) -> dict:
- data = request.data
- assert data is not None
- return json.loads(data.decode("utf-8"))
- def test_login_caches_jwt(monkeypatch):
- calls: list[tuple[str, str, dict | None]] = []
- def fake_urlopen(request, timeout=0):
- body = _decode_body(request) if request.data else None
- calls.append((request.get_method(), request.full_url, body))
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- return FakeResponse({"code": 0, "data": {"items": [], "total": 0, "page": 1, "page_size": 100, "pages": 1}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- first = client._ensure_jwt()
- second = client._ensure_jwt()
- assert first == "jwt-1"
- assert second == "jwt-1"
- assert calls == [
- (
- "POST",
- "http://example.test/api/v1/auth/login",
- {"email": "admin@example.com", "password": "secret"},
- )
- ]
- def test_create_account_sends_correct_payload(monkeypatch):
- seen: dict[str, object] = {}
- def fake_urlopen(request, timeout=0):
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- seen["headers"] = dict(request.header_items())
- seen["body"] = _decode_body(request)
- return FakeResponse({"code": 0, "data": {"id": 9, "name": "demo"}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- payload = client.create_account(
- name="demo",
- credentials={"refresh_token": "rt", "access_token": "at", "email": "demo@example.com"},
- platform="openai",
- type="oauth",
- concurrency=2,
- priority=5,
- )
- assert payload == {"id": 9, "name": "demo"}
- assert seen["body"] == {
- "name": "demo",
- "platform": "openai",
- "type": "oauth",
- "credentials": {"refresh_token": "rt", "access_token": "at", "email": "demo@example.com"},
- "concurrency": 2,
- "priority": 5,
- }
- assert seen["headers"]["Authorization"] == "Bearer jwt-1"
- def test_batch_create(monkeypatch):
- def fake_urlopen(request, timeout=0):
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- assert request.full_url.endswith("/api/v1/admin/accounts/batch")
- assert _decode_body(request) == {"accounts": [{"name": "a"}, {"name": "b"}]}
- return FakeResponse({"code": 0, "data": {"success": 2, "failed": 0, "results": []}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- assert client.batch_create_accounts([{"name": "a"}, {"name": "b"}]) == {"success": 2, "failed": 0, "results": []}
- def test_list_accounts_with_pagination(monkeypatch):
- def fake_urlopen(request, timeout=0):
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- assert request.full_url == (
- "http://example.test/api/v1/admin/accounts?platform=openai&page=2&page_size=50"
- )
- return FakeResponse(
- {"code": 0, "data": {"items": [{"id": 1}], "total": 1, "page": 2, "page_size": 50, "pages": 1}}
- )
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- payload = client.list_accounts(page=2, page_size=50)
- assert payload["items"] == [{"id": 1}]
- assert payload["page"] == 2
- def test_delete_account(monkeypatch):
- def fake_urlopen(request, timeout=0):
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- assert request.get_method() == "DELETE"
- assert request.full_url.endswith("/api/v1/admin/accounts/7")
- return FakeResponse({"code": 0, "data": {"message": "Account deleted successfully"}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- assert client.delete_account(7) is True
- def test_refresh_account(monkeypatch):
- def fake_urlopen(request, timeout=0):
- if request.full_url.endswith("/api/v1/auth/login"):
- return FakeResponse({"code": 0, "data": {"access_token": "jwt-1"}})
- assert request.full_url.endswith("/api/v1/admin/accounts/3/refresh")
- return FakeResponse({"code": 0, "data": {"message": "refresh queued"}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- assert client.refresh_account(3) == {"message": "refresh queued"}
- def test_health_check(monkeypatch):
- def fake_urlopen(request, timeout=0):
- assert request.full_url == "http://example.test/health"
- return FakeResponse({"status": "ok"})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- assert client.health_check() is True
- def test_401_retry_refreshes_jwt(monkeypatch):
- calls: list[str] = []
- def fake_urlopen(request, timeout=0):
- calls.append(request.full_url)
- if request.full_url.endswith("/api/v1/auth/login"):
- token = "jwt-1" if calls.count(request.full_url) == 1 else "jwt-2"
- return FakeResponse({"code": 0, "data": {"access_token": token}})
- auth_header = dict(request.header_items()).get("Authorization")
- if auth_header == "Bearer jwt-1":
- raise FakeHttpError(request.full_url, 401, {"code": 401, "message": "unauthorized"})
- return FakeResponse({"code": 0, "data": {"id": 42}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "admin@example.com", "secret")
- assert client.get_account(42) == {"id": 42}
- assert calls == [
- "http://example.test/api/v1/auth/login",
- "http://example.test/api/v1/admin/accounts/42",
- "http://example.test/api/v1/auth/login",
- "http://example.test/api/v1/admin/accounts/42",
- ]
- def test_api_key_mode_skips_login(monkeypatch):
- seen_headers: list[dict[str, str]] = []
- def fake_urlopen(request, timeout=0):
- seen_headers.append(dict(request.header_items()))
- return FakeResponse({"code": 0, "data": {"items": [], "total": 0, "page": 1, "page_size": 100, "pages": 1}})
- monkeypatch.setattr("ops.sub2api_client.urlopen", fake_urlopen)
- client = Sub2ApiClient("http://example.test", "", "", api_key="key-123")
- client.list_accounts()
- assert len(seen_headers) == 1
- headers = {key.lower(): value for key, value in seen_headers[0].items()}
- assert headers["x-api-key"] == "key-123"
- assert "authorization" not in headers
|