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
|
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:
|
class TestItemCRUD:
|
||||||
"""Test item creation, read, update, delete."""
|
"""Test item creation, read, update, delete."""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user