""" 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