feat(5-plan-02-t1,t6): add backend search endpoint with comprehensive test coverage
This commit is contained in:
@@ -2,6 +2,232 @@ import pytest
|
||||
from fastapi import status
|
||||
|
||||
|
||||
class TestItemSearch:
|
||||
"""Test item search functionality."""
|
||||
|
||||
def test_search_items_by_name_exact_match(self, test_client, test_db, user_token):
|
||||
"""Test exact name match in search."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Resistor 10K",
|
||||
category="Electronics",
|
||||
barcode="RES-10K-001",
|
||||
part_number="R-10K",
|
||||
quantity=100
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=Resistor 10K",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
assert data[0]["name"] == "Resistor 10K"
|
||||
|
||||
def test_search_items_by_part_number(self, test_client, test_db, user_token):
|
||||
"""Test search by part number."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Capacitor",
|
||||
category="Electronics",
|
||||
barcode="CAP-100U-001",
|
||||
part_number="CAP-100UF",
|
||||
quantity=50
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=CAP-100UF",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
assert any(item["part_number"] == "CAP-100UF" for item in data)
|
||||
|
||||
def test_search_items_by_barcode(self, test_client, test_db, user_token):
|
||||
"""Test search by barcode."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Diode",
|
||||
category="Electronics",
|
||||
barcode="BAR-123456789",
|
||||
part_number="D-1N4007",
|
||||
quantity=200
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=BAR-123456789",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
assert any(item["barcode"] == "BAR-123456789" for item in data)
|
||||
|
||||
def test_search_items_by_category(self, test_client, test_db, user_token):
|
||||
"""Test search by category."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Test Item",
|
||||
category="Networking",
|
||||
barcode="NET-001",
|
||||
part_number="NET-PN",
|
||||
quantity=10
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=Networking",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
|
||||
def test_search_items_partial_match(self, test_client, test_db, user_token):
|
||||
"""Test substring matching in search."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Power Supply 500W",
|
||||
category="Power",
|
||||
barcode="PSU-500W",
|
||||
part_number="PSU-500",
|
||||
quantity=5
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=Power",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
|
||||
def test_search_items_no_results(self, test_client, test_db, user_token):
|
||||
"""Test search with no matching results."""
|
||||
response = test_client.get(
|
||||
"/items/search?q=NonexistentItemXYZ",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) == 0
|
||||
|
||||
def test_search_items_empty_query(self, test_client, test_db, user_token):
|
||||
"""Test search with empty query returns empty list."""
|
||||
response = test_client.get(
|
||||
"/items/search?q=",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) == 0
|
||||
|
||||
def test_search_items_max_length_query(self, test_client, test_db, user_token):
|
||||
"""Test search with query exceeding max length returns empty."""
|
||||
long_query = "x" * 101
|
||||
response = test_client.get(
|
||||
f"/items/search?q={long_query}",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) == 0
|
||||
|
||||
def test_search_items_case_insensitive(self, test_client, test_db, user_token):
|
||||
"""Test that search is case-insensitive."""
|
||||
from backend.models import Item
|
||||
|
||||
item = Item(
|
||||
name="Transistor",
|
||||
category="Electronics",
|
||||
barcode="TRN-001",
|
||||
part_number="TRN-2N2222",
|
||||
quantity=75
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=transistor",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
|
||||
def test_search_items_relevance_ordering(self, test_client, test_db, user_token):
|
||||
"""Test that results are ordered by relevance (name match first)."""
|
||||
from backend.models import Item
|
||||
|
||||
# Create items where query matches different fields
|
||||
item1 = Item(
|
||||
name="Resistor",
|
||||
category="Electronics",
|
||||
barcode="RES-100",
|
||||
part_number="R-100K",
|
||||
quantity=100
|
||||
)
|
||||
item2 = Item(
|
||||
name="Component",
|
||||
category="Resistor Components",
|
||||
barcode="RES-200",
|
||||
part_number="R-200K",
|
||||
quantity=50
|
||||
)
|
||||
test_db.add_all([item1, item2])
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=Resistor",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
# Name match should be first
|
||||
assert len(data) >= 1
|
||||
assert data[0]["name"] == "Resistor"
|
||||
|
||||
def test_search_items_max_50_results(self, test_client, test_db, user_token):
|
||||
"""Test that search returns max 50 results."""
|
||||
from backend.models import Item
|
||||
|
||||
# Create 60 items with same category
|
||||
for i in range(60):
|
||||
item = Item(
|
||||
name=f"Item {i}",
|
||||
category="Test",
|
||||
barcode=f"TEST-{i}",
|
||||
part_number=f"P-{i}",
|
||||
quantity=i
|
||||
)
|
||||
test_db.add(item)
|
||||
test_db.commit()
|
||||
|
||||
response = test_client.get(
|
||||
"/items/search?q=Test",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) <= 50
|
||||
|
||||
|
||||
class TestItemCRUD:
|
||||
"""Test item creation, read, update, delete."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user