deepshelf-api / tests /test_personalization_integration.py
nice-bill's picture
initial commit
cdb73a8
import unittest
from unittest.mock import MagicMock, patch
import pandas as pd
from fastapi.testclient import TestClient
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from src.book_recommender.api.main import app, get_recommender
from src.book_recommender.services.personalizer import PersonalizationService
from src.book_recommender.ml.recommender import BookRecommender
class TestPersonalizationIntegration(unittest.TestCase):
def setUp(self):
# Mock Recommender Data
self.mock_recommender = MagicMock(spec=BookRecommender)
self.mock_recommender.book_data = pd.DataFrame({
"id": ["1", "2"],
"title": ["Test Book A", "Test Book B"],
"authors": ["Author A", "Author B"],
"description": ["Desc A", "Desc B"],
"genres": ["Genre A", "Genre B"],
"cover_image_url": ["http://img.com/a.jpg", None]
})
# Mock Personalization Service
self.mock_personalizer = MagicMock(spec=PersonalizationService)
# Override dependencies
app.dependency_overrides[get_recommender] = lambda: self.mock_recommender
# Patch the personalizer instance in the api module
self.patcher = patch("src.book_recommender.api.main.personalizer", self.mock_personalizer)
self.patcher.start()
self.client = TestClient(app)
def tearDown(self):
app.dependency_overrides = {}
self.patcher.stop()
def test_personalize_endpoint_success(self):
# Setup mock return value from personalization service
self.mock_personalizer.get_recommendations.return_value = [
{"title": "Test Book A", "score": 0.9, "genres": "Genre A"},
{"title": "Test Book B", "score": 0.8, "genres": "Genre B"}
]
payload = {
"user_history": ["Some Old Book"],
"top_k": 2
}
response = self.client.post("/recommend/personalize", json=payload)
self.assertEqual(response.status_code, 200)
results = response.json()
self.assertEqual(len(results), 2)
self.assertEqual(results[0]["book"]["title"], "Test Book A")
self.assertEqual(results[0]["similarity_score"], 0.9)
self.assertEqual(results[1]["book"]["title"], "Test Book B")
# Check if cover image logic worked (Book B had None, but we didn't mock load_book_covers_batch so it might stay None or fail if called)
# Actually, load_book_covers_batch is imported in main.py. We might need to mock it if we want to test cover fetching.
# But for basic integration, this is enough.
def test_personalize_endpoint_empty_response(self):
self.mock_personalizer.get_recommendations.return_value = []
payload = {
"user_history": ["Some Old Book"],
"top_k": 2
}
response = self.client.post("/recommend/personalize", json=payload)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [])
def test_personalize_endpoint_service_failure(self):
# Simulate service returning None or raising exception (though wrapper usually catches it)
# The wrapper in main checks "if not semantic_recs: return []"
self.mock_personalizer.get_recommendations.return_value = []
payload = {
"user_history": ["Some Old Book"],
"top_k": 2
}
response = self.client.post("/recommend/personalize", json=payload)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [])
if __name__ == "__main__":
unittest.main()