"""Tests for API endpoints.""" from datetime import datetime from unittest.mock import MagicMock from app.models import EloRating def _fake_listing(**overrides) -> dict: """Return a mock DB row that looks like a joined listing+rating row.""" defaults = dict( global_id="abc-123", tiny_id="123", url="https://example.com/listing", title="Nice House", city="Amsterdam", postcode="1012AB", province="Noord-Holland", neighbourhood="Centrum", municipality="Amsterdam", latitude=52.37, longitude=4.89, object_type="apartment", house_type="upstairs", offering_type="buy", construction_type="existing", construction_year="2000", energy_label="A", living_area=80, plot_area=0, bedrooms=2, rooms=4, has_garden=False, has_balcony=True, has_solar_panels=False, has_heat_pump=False, has_roof_terrace=False, is_energy_efficient=True, is_monument=False, current_price=350000, status="available", price_per_sqm=4375.0, publication_date="2024-01-15", elo_rating=1500.0, comparison_count=0, wins=0, losses=0, ) defaults.update(overrides) row = MagicMock() for k, v in defaults.items(): setattr(row, k, v) return row class TestHealthEndpoint: def test_health(self, client): resp = client.get("/api/health") assert resp.status_code == 200 assert resp.json() == {"status": "ok"} class TestListingsEndpoints: def test_get_listings(self, client, db_session): row = _fake_listing() db_session.execute.return_value = [row] resp = client.get("/api/listings") assert resp.status_code == 200 data = resp.json() assert isinstance(data, list) assert len(data) == 1 assert data[0]["global_id"] == "abc-123" def test_get_listing_not_found(self, client, db_session): result_mock = MagicMock() result_mock.first.return_value = None db_session.execute.return_value = result_mock resp = client.get("/api/listings/nonexistent") assert resp.status_code == 404 def test_get_listing_found(self, client, db_session): row = _fake_listing(global_id="xyz-789") result_mock = MagicMock() result_mock.first.return_value = row db_session.execute.return_value = result_mock resp = client.get("/api/listings/xyz-789") assert resp.status_code == 200 assert resp.json()["global_id"] == "xyz-789" class TestRankingsEndpoints: def test_get_rankings(self, client, db_session): rows = [ _fake_listing(global_id="a", elo_rating=1600.0), _fake_listing(global_id="b", elo_rating=1400.0), ] db_session.execute.return_value = rows resp = client.get("/api/rankings") assert resp.status_code == 200 data = resp.json() assert len(data) == 2 assert data[0]["rank"] == 1 assert data[1]["rank"] == 2 class TestImagesEndpoint: def test_images_found(self, client, db_session): row = MagicMock() row.photo_urls = ["https://img.example.com/1.jpg", "https://img.example.com/2.jpg"] result_mock = MagicMock() result_mock.first.return_value = row db_session.execute.return_value = result_mock resp = client.get("/api/listings/abc-123/images") assert resp.status_code == 200 assert len(resp.json()["images"]) == 2 def test_images_not_found(self, client, db_session): result_mock = MagicMock() result_mock.first.return_value = None db_session.execute.return_value = result_mock resp = client.get("/api/listings/abc-123/images") assert resp.status_code == 200 assert resp.json() == {"images": []} class TestCompareEndpoints: def test_compare_same_ids_rejected(self, client): resp = client.post( "/api/compare", json={"winner_id": "abc", "loser_id": "abc"}, ) assert resp.status_code == 400 def test_compare_success(self, client, db_session): winner = EloRating(global_id="w1", elo_rating=1500.0, comparison_count=0, wins=0, losses=0) loser = EloRating(global_id="l1", elo_rating=1500.0, comparison_count=0, wins=0, losses=0) def fake_filter_by(global_id): mock_result = MagicMock() if global_id == "w1": mock_result.first.return_value = winner elif global_id == "l1": mock_result.first.return_value = loser else: mock_result.first.return_value = None return mock_result db_session.query.return_value.filter_by = fake_filter_by resp = client.post( "/api/compare", json={"winner_id": "w1", "loser_id": "l1"}, ) assert resp.status_code == 200 data = resp.json() assert data["winner_id"] == "w1" assert data["loser_id"] == "l1" assert data["elo_change"] > 0 def test_matchup_insufficient_listings(self, client, db_session): db_session.execute.return_value = [_fake_listing()] # only 1 resp = client.get("/api/matchup") assert resp.status_code == 400 def test_history(self, client, db_session): row = MagicMock() row.id = 1 row.listing_a_title = "House A" row.listing_b_title = "House B" row.winner_title = "House A" row.listing_a_id = "a" row.listing_b_id = "b" row.winner_id = "a" row.elo_a_before = 1500.0 row.elo_b_before = 1500.0 row.elo_a_after = 1516.0 row.elo_b_after = 1484.0 row.created_at = datetime(2024, 1, 1, 12, 0, 0) db_session.execute.return_value = [row] resp = client.get("/api/history") assert resp.status_code == 200 data = resp.json() assert len(data) == 1 assert data[0]["winner_id"] == "a"