from __future__ import annotations import unittest from unittest.mock import MagicMock from iaai_scraper.core.config import Settings from iaai_scraper.scraper import IAAIScraper from iaai_scraper.storage.schemas import CarRecord def make_db_record(origin_id: str) -> dict[str, object]: return CarRecord( parser_id=f"iaai:{origin_id}", brand="Toyota", model="Camry", origin_url=f"https://www.iaai.com/VehicleDetail/{origin_id}~US", origin_id=origin_id, slug=f"toyota-camry-{origin_id}", ).model_dump(mode="json") class TestScraperSync(unittest.TestCase): def _make_scraper(self) -> IAAIScraper: s = Settings() s.log_level = "CRITICAL" return IAAIScraper(s) def test_sync_vehicle_uses_db_record_without_remapping(self) -> None: scraper = self._make_scraper() scraper.persistence.create_tables = MagicMock() scraper.persistence.start_sync_run = MagicMock(return_value=1) scraper.persistence.finish_sync_run = MagicMock() scraper.persistence.upsert_car = MagicMock(return_value={"action": "inserted", "images_upserted": 0}) scraper.scrape_vehicle_detail = MagicMock(return_value={"db_record": make_db_record("111")}) scraper.car_mapper.map_to_car_record = MagicMock(side_effect=AssertionError("should not be called")) result = scraper.sync_vehicle("https://www.iaai.com/VehicleDetail/111~US") self.assertEqual(result["status"], "success") self.assertIn("trace_id", result) self.assertIn("elapsed_seconds", result) self.assertEqual(scraper.persistence.upsert_car.call_count, 1) def test_sync_listing_uses_db_record_without_remapping(self) -> None: scraper = self._make_scraper() scraper.persistence.create_tables = MagicMock() scraper.persistence.start_sync_run = MagicMock(return_value=2) scraper.persistence.finish_sync_run = MagicMock() scraper.persistence.upsert_car = MagicMock(return_value={"action": "inserted", "images_upserted": 1}) scraper.collect_listing = MagicMock(return_value={"vehicle_urls": ["https://www.iaai.com/VehicleDetail/222~US"]}) scraper._scrape_on_page = MagicMock(return_value={"db_record": make_db_record("222")}) scraper._get_page = MagicMock(return_value=MagicMock()) scraper.car_mapper.map_to_car_record = MagicMock(side_effect=AssertionError("should not be called")) result = scraper.sync_listing() self.assertEqual(result["cars_upserted"], 1) self.assertEqual(result["cars_failed"], 0) self.assertIn("trace_id", result) self.assertIn("elapsed_seconds", result) self.assertEqual(scraper.persistence.upsert_car.call_count, 1) def test_sync_listing_respects_limit(self) -> None: scraper = self._make_scraper() scraper.persistence.create_tables = MagicMock() scraper.persistence.start_sync_run = MagicMock(return_value=3) scraper.persistence.finish_sync_run = MagicMock() scraper.persistence.upsert_car = MagicMock(return_value={"action": "inserted", "images_upserted": 1}) scraper.collect_listing = MagicMock(return_value={ "vehicle_urls": [ "https://www.iaai.com/VehicleDetail/222~US", "https://www.iaai.com/VehicleDetail/333~US", ] }) scraper._scrape_on_page = MagicMock(return_value={"db_record": make_db_record("222")}) scraper._get_page = MagicMock(return_value=MagicMock()) scraper.sync_listing(limit=1) self.assertEqual(scraper._scrape_on_page.call_count, 1) def test_close_resets_browser_state(self) -> None: scraper = self._make_scraper() scraper.context = MagicMock() scraper.browser = MagicMock() scraper.playwright = MagicMock() scraper.close() self.assertIsNone(scraper.context) self.assertIsNone(scraper.browser) self.assertIsNone(scraper.playwright) if __name__ == "__main__": unittest.main()