from types import SimpleNamespace import pytest from fastapi.testclient import TestClient from backend import app as api, data_loader from backend.tests.test_sixth_form_flag import _schools_df def client_for(monkeypatch, search): client = SimpleNamespace(collections={'schools': SimpleNamespace(documents=SimpleNamespace(search=search))}) monkeypatch.setattr(data_loader, '_get_typesense_client', lambda: client) def test_search_returns_matches_beyond_first_page(monkeypatch): pages = [] def search(params): pages.append(params['page']) urns = range(100000, 100250) if params['page'] == 1 else [100999] return {'found': 251, 'hits': [{'document': {'urn': u}} for u in urns]} client_for(monkeypatch, search) result = data_loader.search_schools_typesense('academy') assert len(result) == 251 assert result[-1] == 100999 assert pages == [1, 2] def test_later_page_failure_does_not_return_partial_results(monkeypatch): def search(params): if params['page'] == 2: raise RuntimeError('timeout') return {'found': 251, 'hits': [{'document': {'urn': u}} for u in range(100000, 100250)]} client_for(monkeypatch, search) assert data_loader.search_schools_typesense('academy') is None def test_zero_matches_are_distinct_from_unavailable(monkeypatch): client_for(monkeypatch, lambda _: {'found': 0, 'hits': []}) assert data_loader.search_schools_typesense('academy') == [] monkeypatch.setattr(data_loader, '_get_typesense_client', lambda: None) assert data_loader.search_schools_typesense('academy') is None @pytest.mark.parametrize('matches, expected', [([], []), (None, [100001])]) def test_fallback_only_on_dependency_failure(monkeypatch, matches, expected): monkeypatch.setattr(api.limiter, 'enabled', False) monkeypatch.setattr(api, 'load_latest_school_data', _schools_df) monkeypatch.setattr(api, 'search_schools_typesense', lambda _: matches) response = TestClient(api.app).get('/api/schools?search=Alpha') assert response.status_code == 200 assert [s['urn'] for s in response.json()['schools']] == expected def test_filtered_api_keeps_match_from_second_search_page(monkeypatch): monkeypatch.setattr(api.limiter, 'enabled', False) df = _schools_df() monkeypatch.setattr(api, 'load_latest_school_data', lambda: df) def search(params): urns = range(200000, 200250) if params['page'] == 1 else [100001] return {'found': 251, 'hits': [{'document': {'urn': u}} for u in urns]} client_for(monkeypatch, search) response = TestClient(api.app).get('/api/schools?search=Alpha&local_authority=Testshire') assert response.status_code == 200 assert response.json()['total'] == 1 assert response.json()['schools'][0]['urn'] == 100001 def test_unavailable_dataset_is_not_a_missing_school_or_empty_search(monkeypatch): import pandas as pd monkeypatch.setattr(api.limiter, 'enabled', False) monkeypatch.setattr(api, 'load_school_data', lambda: pd.DataFrame()) monkeypatch.setattr(api, 'load_latest_school_data', lambda: pd.DataFrame()) client = TestClient(api.app) assert client.get('/api/schools/100001').status_code == 503 assert client.get('/api/schools?search=school').status_code == 503