feat: filter disliked listings from GeoJSON endpoints
Both /api/listing_geojson and /api/listing_geojson/stream now exclude disliked listings by default. A decision_filter='everything' param bypasses filtering. 2 integration tests verify the behavior.
This commit is contained in:
parent
43084ef19a
commit
8452f65d25
2 changed files with 165 additions and 2 deletions
121
tests/unit/test_decision_filtering.py
Normal file
121
tests/unit/test_decision_filtering.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
"""Test that disliked listings are filtered from the GeoJSON endpoint."""
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from sqlalchemy import Engine
|
||||
from sqlmodel import SQLModel, Session, create_engine
|
||||
|
||||
from models.user import User
|
||||
from models.listing import RentListing, ListingSite, FurnishType
|
||||
from models.decision import ListingDecision
|
||||
from api.auth import get_current_user, User as AuthUser
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def filter_engine() -> Engine:
|
||||
engine = create_engine(
|
||||
"sqlite:///:memory:",
|
||||
echo=False,
|
||||
connect_args={"check_same_thread": False},
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
with Session(engine) as session:
|
||||
session.add(User(id=1, email="test@example.com"))
|
||||
# Add two listings
|
||||
now = datetime.now()
|
||||
for lid in [100, 200]:
|
||||
session.add(RentListing(
|
||||
id=lid,
|
||||
price=2000.0,
|
||||
number_of_bedrooms=2,
|
||||
square_meters=50.0,
|
||||
longitude=-0.1,
|
||||
latitude=51.5,
|
||||
price_history_json="[]",
|
||||
listing_site=ListingSite.RIGHTMOVE,
|
||||
last_seen=now,
|
||||
floorplan_image_paths=[],
|
||||
additional_info={"property": {"visible": True}},
|
||||
furnish_type=FurnishType.FURNISHED,
|
||||
))
|
||||
# Dislike listing 200
|
||||
session.add(ListingDecision(
|
||||
user_id=1,
|
||||
listing_id=200,
|
||||
listing_type="RENT",
|
||||
decision="disliked",
|
||||
))
|
||||
session.commit()
|
||||
yield engine # type: ignore[misc]
|
||||
SQLModel.metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def filter_client(filter_engine: Engine) -> AsyncClient:
|
||||
import database
|
||||
import api.app as api_app
|
||||
import api.decision_routes as decision_routes_mod
|
||||
import api.poi_routes as poi_routes_mod
|
||||
|
||||
app = api_app.app
|
||||
mock_user = AuthUser(
|
||||
sub="test", email="test@example.com", name="Test"
|
||||
)
|
||||
app.dependency_overrides[get_current_user] = lambda: mock_user
|
||||
|
||||
original_db = database.engine
|
||||
original_app = api_app.engine
|
||||
original_decision = decision_routes_mod.engine
|
||||
original_poi = poi_routes_mod.engine
|
||||
database.engine = filter_engine
|
||||
api_app.engine = filter_engine
|
||||
decision_routes_mod.engine = filter_engine
|
||||
poi_routes_mod.engine = filter_engine
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c # type: ignore[misc]
|
||||
|
||||
database.engine = original_db
|
||||
api_app.engine = original_app
|
||||
decision_routes_mod.engine = original_decision
|
||||
poi_routes_mod.engine = original_poi
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
class TestDecisionFiltering:
|
||||
@pytest.mark.asyncio
|
||||
async def test_disliked_excluded_by_default(
|
||||
self, filter_client: AsyncClient
|
||||
) -> None:
|
||||
"""Default decision_filter should exclude disliked listings."""
|
||||
resp = await filter_client.get(
|
||||
"/api/listing_geojson",
|
||||
params={"listing_type": "RENT"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
listing_ids = [
|
||||
f["properties"]["url"].split("/")[-1]
|
||||
for f in data["features"]
|
||||
]
|
||||
assert "100" in listing_ids
|
||||
assert "200" not in listing_ids
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_everything_filter_includes_all(
|
||||
self, filter_client: AsyncClient
|
||||
) -> None:
|
||||
"""decision_filter='everything' should include disliked listings."""
|
||||
resp = await filter_client.get(
|
||||
"/api/listing_geojson",
|
||||
params={"listing_type": "RENT", "decision_filter": "everything"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
listing_ids = [
|
||||
f["properties"]["url"].split("/")[-1]
|
||||
for f in data["features"]
|
||||
]
|
||||
assert "100" in listing_ids
|
||||
assert "200" in listing_ids
|
||||
Loading…
Add table
Add a link
Reference in a new issue