| from unittest.mock import MagicMock |
| import pandas as pd |
| from fastapi.testclient import TestClient |
|
|
| from backend.app.main import app |
|
|
|
|
| def _make_search_results(): |
| df = pd.DataFrame([{ |
| "sku": "12345", |
| "name": "Test Dress", |
| "brand": "ASOS", |
| "price": 29.99, |
| "color_clean": "black", |
| "color_family": "black", |
| "category": "Dresses", |
| "gender": "Women", |
| "primary_image_url": "https://example.com/img.jpg", |
| "hybrid_score": 0.95, |
| "style_tags": ["casual"], |
| "any_in_stock": True, |
| }]) |
| df.attrs["query_info"] = { |
| "original_query": "black dress", |
| "processed_query": "black dress", |
| "detected_language": "en", |
| "was_translated": False, |
| "was_spell_corrected": False, |
| "spell_suggestion": None, |
| "parsed_category": "Dresses", |
| "parsed_color": "black", |
| "parsed_price_range": [None, None], |
| "parsed_gender": None, |
| "parsed_style_tags": [], |
| "parsed_material": None, |
| "parsed_size": None, |
| "parsed_exclusions": [], |
| "sort_by": "relevance", |
| "available_sorts": ["relevance", "price_asc", "price_desc"], |
| "suggested_searches": ["navy dresses"], |
| } |
| return df |
|
|
|
|
| def _make_mock_engine(): |
| engine = MagicMock() |
| engine._is_ready = True |
| engine.search.return_value = _make_search_results() |
| return engine |
|
|
|
|
| class TestSearchEndpoints: |
| def test_text_search(self): |
| app.state.engine = _make_mock_engine() |
| client = TestClient(app, raise_server_exceptions=False) |
| response = client.post("/api/v1/search", json={"query": "black dress"}) |
| assert response.status_code == 200 |
| data = response.json() |
| assert data["total"] == 1 |
| assert data["results"][0]["sku"] == "12345" |
| assert data["results"][0]["name"] == "Test Dress" |
| assert data["query_info"]["parsed_category"] == "Dresses" |
|
|
| def test_search_with_params(self): |
| app.state.engine = _make_mock_engine() |
| client = TestClient(app, raise_server_exceptions=False) |
| response = client.post("/api/v1/search", json={ |
| "query": "red shoes", |
| "top_n": 5, |
| "sort_by": "price_asc", |
| }) |
| assert response.status_code == 200 |
|
|
| def test_empty_query_rejected(self): |
| app.state.engine = _make_mock_engine() |
| client = TestClient(app, raise_server_exceptions=False) |
| response = client.post("/api/v1/search", json={"query": ""}) |
| assert response.status_code == 422 |
|
|
| def test_engine_not_ready(self): |
| app.state.engine = None |
| client = TestClient(app, raise_server_exceptions=False) |
| response = client.post("/api/v1/search", json={"query": "dress"}) |
| assert response.status_code == 503 |
|
|
| def test_similar_search(self): |
| engine = _make_mock_engine() |
| engine.get_product_detail.return_value = { |
| "sku": "12345", "name": "Test Dress", "brand": "ASOS", |
| "price": 29.99, "color_clean": "black", "color_family": "black", |
| "category": "Dresses", "gender": "Women", |
| "primary_image_url": "https://example.com/img.jpg", |
| "image_urls": [], "style_tags": [], "materials": [], |
| "sizes_available": [], "product_details": "", "any_in_stock": True, |
| } |
| engine.search_similar.return_value = pd.DataFrame([{ |
| "sku": "67890", "name": "Similar Dress", "brand": "ASOS", |
| "price": 35.00, "color_clean": "navy", "category": "Dresses", |
| "primary_image_url": "https://example.com/img2.jpg", |
| "similarity_score": 0.89, |
| }]) |
| app.state.engine = engine |
| client = TestClient(app, raise_server_exceptions=False) |
| response = client.get("/api/v1/search/similar/12345") |
| assert response.status_code == 200 |
| data = response.json() |
| assert data["total"] == 1 |
| assert data["results"][0]["similarity_score"] == 0.89 |
|
|