a69a26852e
* Cut down unnecessary logging * fix format etc * fix checks * fix tests
136 lines
4.6 KiB
Python
136 lines
4.6 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from api.v1.schemas.search import SuggestResponse, SuggestResult, SearchResponse, SearchResult
|
|
from api.v1.routes.search import router
|
|
from core.dependencies import get_search_service, get_coverart_repository, get_search_enrichment_service
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_search_service():
|
|
mock_svc = MagicMock()
|
|
mock_svc.suggest = AsyncMock(return_value=SuggestResponse(results=[]))
|
|
return mock_svc
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mock_search_service):
|
|
test_app = FastAPI()
|
|
test_app.include_router(router)
|
|
test_app.dependency_overrides[get_search_service] = lambda: mock_search_service
|
|
return TestClient(test_app)
|
|
|
|
|
|
def test_suggest_rejects_single_char_query(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=a")
|
|
|
|
assert response.status_code == 422
|
|
mock_search_service.suggest.assert_not_called()
|
|
|
|
|
|
def test_suggest_rejects_empty_query(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=")
|
|
|
|
assert response.status_code == 422
|
|
mock_search_service.suggest.assert_not_called()
|
|
|
|
|
|
def test_suggest_rejects_missing_query(client, mock_search_service):
|
|
response = client.get("/search/suggest")
|
|
|
|
assert response.status_code == 422
|
|
mock_search_service.suggest.assert_not_called()
|
|
|
|
|
|
def test_suggest_accepts_two_char_query(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=ab")
|
|
|
|
assert response.status_code == 200
|
|
assert response.json() == {"results": []}
|
|
|
|
|
|
def test_suggest_limit_lower_bound(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=test&limit=0")
|
|
|
|
assert response.status_code == 422
|
|
|
|
|
|
def test_suggest_limit_upper_bound(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=test&limit=11")
|
|
|
|
assert response.status_code == 422
|
|
|
|
|
|
def test_suggest_limit_defaults_to_five(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=test")
|
|
|
|
assert response.status_code == 200
|
|
mock_search_service.suggest.assert_called_once_with(query="test", limit=5)
|
|
|
|
|
|
def test_suggest_custom_limit(client, mock_search_service):
|
|
response = client.get("/search/suggest?q=test&limit=3")
|
|
|
|
assert response.status_code == 200
|
|
mock_search_service.suggest.assert_called_once_with(query="test", limit=3)
|
|
|
|
|
|
def test_suggest_whitespace_padded_short_input_returns_empty(client, mock_search_service):
|
|
"""Whitespace-padded query that is < 2 chars after strip returns empty at route level."""
|
|
response = client.get("/search/suggest?q=%20%20a%20%20")
|
|
|
|
assert response.status_code == 200
|
|
assert response.json() == {"results": []}
|
|
mock_search_service.suggest.assert_not_called()
|
|
|
|
|
|
def test_suggest_whitespace_padded_valid_input_strips(client, mock_search_service):
|
|
"""Whitespace-padded query that is >= 2 chars after strip passes stripped value to service."""
|
|
response = client.get("/search/suggest?q=%20%20ab%20%20")
|
|
|
|
assert response.status_code == 200
|
|
mock_search_service.suggest.assert_called_once_with(query="ab", limit=5)
|
|
|
|
|
|
def test_search_response_tolerates_additive_score_field():
|
|
"""Existing /api/search consumers tolerate the additive score field on SearchResult."""
|
|
mock_search_service = MagicMock()
|
|
mock_search_service.search = AsyncMock(return_value=SearchResponse(
|
|
artists=[
|
|
SearchResult(
|
|
type="artist", title="Muse", musicbrainz_id="mb-1",
|
|
in_library=False, requested=False, score=90,
|
|
)
|
|
],
|
|
albums=[
|
|
SearchResult(
|
|
type="album", title="Absolution", musicbrainz_id="mb-2",
|
|
artist="Muse", in_library=True, requested=False, score=85,
|
|
)
|
|
],
|
|
))
|
|
mock_search_service.schedule_cover_prefetch = MagicMock(return_value=[])
|
|
|
|
mock_coverart = MagicMock()
|
|
mock_enrichment = MagicMock()
|
|
|
|
test_app = FastAPI()
|
|
test_app.include_router(router)
|
|
test_app.dependency_overrides[get_search_service] = lambda: mock_search_service
|
|
test_app.dependency_overrides[get_coverart_repository] = lambda: mock_coverart
|
|
test_app.dependency_overrides[get_search_enrichment_service] = lambda: mock_enrichment
|
|
search_client = TestClient(test_app)
|
|
|
|
response = search_client.get("/search?q=muse")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "artists" in data
|
|
assert "albums" in data
|
|
assert data["artists"][0]["score"] == 90
|
|
assert data["albums"][0]["score"] == 85
|
|
assert data["artists"][0]["title"] == "Muse"
|