app.bettersight.io/backend/tests/repositories/test_pocketbase_client.py

158 lines
5.0 KiB
Python

"""
repositories/pocketbase_client.py is the single chokepoint every real
repository depends on — fully unit tested here against a mocked
`requests` module so no live PocketBase instance is needed.
"""
import pytest
from repositories.pocketbase_client import PocketBaseClient, PocketBaseError
class _FakeResponse:
def __init__(self, status_code=200, json_data=None, text='', content=b'1'):
self.status_code = status_code
self._json = json_data or {}
self.text = text
self.content = content if status_code < 400 else (content or b'err')
def json(self):
return self._json
def test_authenticate_caches_token(monkeypatch):
client = PocketBaseClient()
calls = []
def _fake_post(url, json, timeout):
calls.append(url)
return _FakeResponse(200, {'token': 'abc123'})
monkeypatch.setattr('repositories.pocketbase_client.requests.post', _fake_post)
client._authenticate()
assert client._token == 'abc123'
assert len(calls) == 1
def test_authenticate_raises_on_failure(monkeypatch):
client = PocketBaseClient()
monkeypatch.setattr(
'repositories.pocketbase_client.requests.post',
lambda *a, **k: _FakeResponse(400, text='bad credentials')
)
with pytest.raises(PocketBaseError):
client._authenticate()
def test_request_retries_once_on_401(monkeypatch):
client = PocketBaseClient()
client._token = 'stale-token'
client._token_expires_at = 9999999999
responses = [_FakeResponse(401), _FakeResponse(200, {'items': []})]
calls = {'auth': 0, 'request': 0}
def _fake_authenticate():
calls['auth'] += 1
client._token = 'fresh-token'
def _fake_request(method, url, headers, timeout, **kwargs):
calls['request'] += 1
return responses.pop(0)
monkeypatch.setattr(client, '_authenticate', _fake_authenticate)
monkeypatch.setattr('repositories.pocketbase_client.requests.request', _fake_request)
result = client._request('GET', '/api/collections/tenants/records')
assert calls['auth'] == 1
assert calls['request'] == 2
assert result == {'items': []}
def test_request_raises_on_non_401_error(monkeypatch):
client = PocketBaseClient()
client._token = 'token'
client._token_expires_at = 9999999999
monkeypatch.setattr(
'repositories.pocketbase_client.requests.request',
lambda *a, **k: _FakeResponse(500, text='server error')
)
with pytest.raises(PocketBaseError):
client._request('GET', '/api/collections/tenants/records')
def test_list_returns_items(monkeypatch):
client = PocketBaseClient()
monkeypatch.setattr(client, '_request', lambda *a, **k: {'items': [{'id': '1'}], 'page': 1})
assert client.list('tenants') == [{'id': '1'}]
def test_get_first_returns_none_when_empty(monkeypatch):
client = PocketBaseClient()
monkeypatch.setattr(client, 'list', lambda *a, **k: [])
assert client.get_first('tenants', 'domain = "x"') is None
def test_get_one_returns_none_on_404(monkeypatch):
client = PocketBaseClient()
def _raise_404(*a, **k):
raise PocketBaseError('GET /x failed: 404 not found')
monkeypatch.setattr(client, '_request', _raise_404)
assert client.get_one('tenants', 'missing-id') is None
def test_get_one_reraises_non_404_errors(monkeypatch):
client = PocketBaseClient()
def _raise_500(*a, **k):
raise PocketBaseError('GET /x failed: 500 server error')
monkeypatch.setattr(client, '_request', _raise_500)
with pytest.raises(PocketBaseError):
client.get_one('tenants', 'some-id')
def test_create_update_delete_delegate_to_request(monkeypatch):
client = PocketBaseClient()
captured = []
monkeypatch.setattr(client, '_request', lambda method, path, **k: captured.append((method, path, k)) or {'id': '1'})
client.create('tenants', {'name': 'x'})
client.update('tenants', '1', {'name': 'y'})
client.delete('tenants', '1')
assert captured[0][0] == 'POST'
assert captured[1][0] == 'PATCH'
assert captured[2][0] == 'DELETE'
def test_auth_refresh_returns_record_on_success(monkeypatch):
client = PocketBaseClient()
monkeypatch.setattr(
'repositories.pocketbase_client.requests.post',
lambda *a, **k: _FakeResponse(200, {'record': {'email': 'pm@gadventures.com'}})
)
assert client.auth_refresh('sometoken') == {'email': 'pm@gadventures.com'}
def test_auth_refresh_returns_none_on_failure(monkeypatch):
client = PocketBaseClient()
monkeypatch.setattr(
'repositories.pocketbase_client.requests.post',
lambda *a, **k: _FakeResponse(401)
)
assert client.auth_refresh('badtoken') is None
def test_auth_refresh_returns_none_on_network_error(monkeypatch):
import requests
client = PocketBaseClient()
def _raise(*a, **k):
raise requests.RequestException('timeout')
monkeypatch.setattr('repositories.pocketbase_client.requests.post', _raise)
assert client.auth_refresh('sometoken') is None