wrongmove/tests/unit/test_listing_processor.py
Viktor Barzin e5ce8c1201
Fix buy listing support: thread ListingType through processing pipeline
The listing processor was hardcoded to create RentListing objects and
query only the rentlisting table. Buy listings fetched from Rightmove
were stored in the wrong table with missing fields. This threads
ListingType through ListingProcessor and all Step subclasses so the
correct model (RentListing/BuyListing) is created, the correct table
is queried, and buy-specific fields (service_charge, lease_left) are
parsed from the API response and included in GeoJSON streaming output.
2026-02-07 23:34:08 +00:00

87 lines
3 KiB
Python

"""Unit tests for the listing processor."""
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from models.listing import FurnishType, ListingType
from listing_processor import (
_parse_furnish_type,
_parse_available_from,
ListingProcessor,
FetchListingDetailsStep,
MAX_OCR_WORKERS,
)
class TestParseFurnishType:
"""Tests for _parse_furnish_type helper."""
def test_none_returns_unknown(self):
assert _parse_furnish_type(None) == FurnishType.UNKNOWN
def test_ask_landlord_variant(self):
assert _parse_furnish_type("Ask landlord") == FurnishType.ASK_LANDLORD
def test_furnished_lowercased(self):
assert _parse_furnish_type("Furnished") == FurnishType.FURNISHED
def test_unfurnished(self):
assert _parse_furnish_type("Unfurnished") == FurnishType.UNFURNISHED
def test_part_furnished(self):
assert _parse_furnish_type("Part Furnished") == FurnishType.PART_FURNISHED
def test_unknown_string_returns_unknown(self):
assert _parse_furnish_type("unknown") == FurnishType.UNKNOWN
def test_garbage_string_returns_unknown(self):
assert _parse_furnish_type("xyzzy") == FurnishType.UNKNOWN
class TestParseAvailableFrom:
"""Tests for _parse_available_from helper."""
def test_none_returns_none(self):
assert _parse_available_from(None) is None
def test_now_returns_datetime(self):
result = _parse_available_from("Now")
assert isinstance(result, datetime)
def test_valid_date_string(self):
result = _parse_available_from("15/03/2024")
assert result is not None
assert result.day == 15
assert result.month == 3
def test_invalid_date_returns_none(self):
assert _parse_available_from("invalid") is None
class TestListingProcessor:
"""Tests for ListingProcessor."""
async def test_process_listing_marks_seen(self):
"""Test that process_listing calls mark_seen."""
mock_repo = AsyncMock()
mock_repo.get_listings = AsyncMock(return_value=[MagicMock()])
processor = ListingProcessor(mock_repo)
# Mock all steps to not need processing
for step in processor.process_steps:
step.needs_processing = AsyncMock(return_value=False)
await processor.process_listing(123)
mock_repo.mark_seen.assert_awaited_once_with(123, ListingType.RENT)
async def test_process_listing_returns_none_on_step_failure(self):
"""Test that a step failure returns None."""
mock_repo = AsyncMock()
processor = ListingProcessor(mock_repo)
for step in processor.process_steps:
step.needs_processing = AsyncMock(return_value=True)
step.process = AsyncMock(side_effect=Exception("fail"))
result = await processor.process_listing(123)
assert result is None
class TestOcrWorkersConfig:
def test_max_ocr_workers_positive(self):
assert MAX_OCR_WORKERS >= 1