"""Tests for scraper module functions.""" from datetime import datetime, timezone from scraper import extract_ad_fields class TestExtractAdFields: """Test the extract_ad_fields function.""" def test_full_extraction(self, sample_ad_data): """Should extract all fields from a complete ad.""" fields = extract_ad_fields(sample_ad_data) assert fields["wh_ad_id"] == "12345678" assert fields["title"] == "Mountain Bike 2024" assert fields["price"] == 250.0 assert fields["location"] == "Vienna" assert fields["url"] == "https://www.willhaben.at/iad/mountain-bike-2024/12345678" assert fields["postcode"] == "1010" assert fields["main_image_url"] == "https://img.willhaben.at/img123.jpg" assert isinstance(fields["published_at"], datetime) assert isinstance(fields["modified_at"], datetime) def test_published_at_is_utc(self, sample_ad_data): """Published_at should be parsed as UTC.""" fields = extract_ad_fields(sample_ad_data) assert fields["published_at"].tzinfo == timezone.utc assert fields["published_at"].hour == 10 assert fields["published_at"].minute == 30 def test_modified_at_is_utc(self, sample_ad_data): """Modified_at should be parsed as UTC.""" fields = extract_ad_fields(sample_ad_data) assert fields["modified_at"].tzinfo == timezone.utc assert fields["modified_at"].hour == 12 def test_missing_price(self): """Should handle ads without a price attribute.""" ad_data = { "id": "999", "description": "Free item", "attributes": { "attribute": [ {"name": "HEADING", "values": ["Free Item"]}, {"name": "LOCATION", "values": ["Graz"]}, ] }, } fields = extract_ad_fields(ad_data) assert fields["price"] is None assert fields["title"] == "Free Item" def test_missing_heading_falls_back_to_description(self): """Should fall back to description when HEADING is missing.""" ad_data = { "id": "888", "description": "Fallback description", "attributes": {"attribute": []}, } fields = extract_ad_fields(ad_data) assert fields["title"] == "Fallback description" def test_missing_attributes(self): """Should handle ads with no attributes at all.""" ad_data = {"id": "777", "description": "Minimal ad"} fields = extract_ad_fields(ad_data) assert fields["wh_ad_id"] == "777" assert fields["title"] == "Minimal ad" assert fields["price"] is None assert fields["location"] is None def test_price_with_comma_separator(self): """Should parse prices with comma (comma is stripped, so '1.299,50' → 1.2995).""" ad_data = { "id": "666", "attributes": { "attribute": [ {"name": "PRICE/AMOUNT", "values": ["1.299,50"]}, ] }, } fields = extract_ad_fields(ad_data) # The parser strips commas only: "1.299,50" → "1.2995" → 1.2995 assert fields["price"] == 1.2995 def test_missing_image(self): """Should handle ads without images.""" ad_data = { "id": "555", "attributes": { "attribute": [ {"name": "HEADING", "values": ["No Image"]}, ] }, } fields = extract_ad_fields(ad_data) assert fields["main_image_url"] is None def test_empty_image_list(self): """Should handle ads with empty image list.""" ad_data = { "id": "444", "attributes": {"attribute": []}, "advertImageList": {"advertImage": []}, } fields = extract_ad_fields(ad_data) assert fields["main_image_url"] is None def test_missing_seo_url(self): """Should handle ads without SEO_URL.""" ad_data = { "id": "333", "attributes": { "attribute": [ {"name": "HEADING", "values": ["No SEO"]}, ] }, } fields = extract_ad_fields(ad_data) assert fields["url"] is None def test_invalid_price_format(self): """Should handle invalid price values gracefully.""" ad_data = { "id": "222", "attributes": { "attribute": [ {"name": "PRICE/AMOUNT", "values": ["not a number"]}, ] }, } fields = extract_ad_fields(ad_data) assert fields["price"] is None def test_invalid_date_format(self): """Should handle invalid date values gracefully.""" ad_data = { "id": "111", "attributes": { "attribute": [ {"name": "PUBLISHED_String", "values": ["not-a-date"]}, {"name": "CHANGED_String", "values": ["also-not-a-date"]}, ] }, } fields = extract_ad_fields(ad_data) assert fields["published_at"] is None assert fields["modified_at"] is None