wrongmove/crawler/tests/unit/test_models.py

343 lines
11 KiB
Python

"""Unit tests for Listing models."""
from datetime import datetime
import json
import pytest
from models.listing import (
BuyListing,
FurnishType,
ListingSite,
PriceHistoryItem,
RentListing,
Listing,
)
class TestListing:
"""Tests for the base Listing model."""
def test_price_per_square_meter_calculation(self) -> None:
"""Test that price_per_square_meter is calculated correctly."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.price_per_square_meter == 40.0
def test_price_per_square_meter_none_when_no_sqm(self) -> None:
"""Test that price_per_square_meter is None when square_meters is None."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=None,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.price_per_square_meter is None
def test_price_per_square_meter_none_when_sqm_zero(self) -> None:
"""Test that price_per_square_meter is None when square_meters is 0."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=0.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.price_per_square_meter is None
def test_url_property(self) -> None:
"""Test that url property returns correct Rightmove URL."""
listing = RentListing(
id=123456789,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.url == "https://www.rightmove.co.uk/properties/123456789"
def test_is_removed_property_visible(self) -> None:
"""Test that is_removed returns False when property is visible."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.is_removed is False
def test_is_removed_property_not_visible(self) -> None:
"""Test that is_removed returns True when property is not visible."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": False}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.is_removed is True
class TestPriceHistory:
"""Tests for price history serialization/deserialization."""
def test_price_history_serialization_roundtrip(self) -> None:
"""Test that price history can be serialized and deserialized."""
now = datetime.now()
price_history = [
PriceHistoryItem(
first_seen=now,
last_seen=now,
price=2000.0,
),
PriceHistoryItem(
first_seen=now,
last_seen=now,
price=2100.0,
),
]
# Serialize
serialized = Listing.serialize_price_history(price_history)
assert isinstance(serialized, str)
# Create listing with serialized history
listing = RentListing(
id=1,
price=2100.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json=serialized,
listing_site=ListingSite.RIGHTMOVE,
last_seen=now,
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
# Deserialize
deserialized = listing.price_history
assert len(deserialized) == 2
assert deserialized[0].price == 2000.0
assert deserialized[1].price == 2100.0
def test_price_history_empty(self) -> None:
"""Test that empty price history works correctly."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=None,
)
assert listing.price_history == []
def test_price_history_item_to_dict(self) -> None:
"""Test PriceHistoryItem.to_dict() method."""
now = datetime.now()
item = PriceHistoryItem(
first_seen=now,
last_seen=now,
price=2500.0,
)
result = item.to_dict()
assert result["price"] == 2500.0
assert result["first_seen"] == now.isoformat()
assert result["last_seen"] == now.isoformat()
class TestRentListing:
"""Tests specific to RentListing model."""
def test_rent_listing_has_furnish_type(self) -> None:
"""Test that RentListing has furnish_type field."""
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.PART_FURNISHED,
available_from=None,
)
assert listing.furnish_type == FurnishType.PART_FURNISHED
def test_rent_listing_has_available_from(self) -> None:
"""Test that RentListing has available_from field."""
now = datetime.now()
listing = RentListing(
id=1,
price=2000.0,
number_of_bedrooms=2,
square_meters=50.0,
agency="Test",
council_tax_band="C",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=now,
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=now,
)
assert listing.available_from == now
class TestBuyListing:
"""Tests specific to BuyListing model."""
def test_buy_listing_has_service_charge(self) -> None:
"""Test that BuyListing has service_charge field."""
listing = BuyListing(
id=1,
price=450000.0,
number_of_bedrooms=3,
square_meters=95.0,
agency="Test",
council_tax_band="D",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
service_charge=2500.0,
lease_left=85,
)
assert listing.service_charge == 2500.0
def test_buy_listing_has_lease_left(self) -> None:
"""Test that BuyListing has lease_left field."""
listing = BuyListing(
id=1,
price=450000.0,
number_of_bedrooms=3,
square_meters=95.0,
agency="Test",
council_tax_band="D",
longitude=0.0,
latitude=0.0,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail=None,
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
service_charge=None,
lease_left=120,
)
assert listing.lease_left == 120