from __future__ import annotations import unittest from openlane_scraper.openlane.mapper import ( map_openlane_record, map_openlane_records, _parse_mileage, _parse_engine_volume_cc, _generate_slug, ) class TestMapOpenLaneRecord(unittest.TestCase): def _sample_record(self, **overrides) -> dict: base = { "id": "12345", "make": "Toyota", "model": "Camry", "year": 2020, "mileage": 45000, "price": 18500, "color": "White", "body_type": "sedan", "transmission": "automatic", "drivetrain": "fwd", "vin": "1HGBH41JXMN109186", "url": "https://app.openlane.com/vehicles/12345", "images": [ {"full": "https://img.openlane.com/1_full.jpg", "thumbnail": "https://img.openlane.com/1_thumb.jpg"}, {"full": "https://img.openlane.com/2_full.jpg", "thumbnail": "https://img.openlane.com/2_thumb.jpg"}, ], } base.update(overrides) return base def test_basic_mapping(self) -> None: record = self._sample_record() result = map_openlane_record(record) self.assertIsNotNone(result) self.assertEqual(result.brand, "TOYOTA") self.assertEqual(result.model, "CAMRY") self.assertEqual(result.year, 2020) self.assertEqual(result.price, 18500) self.assertEqual(result.mileage, 45000) self.assertEqual(result.color, "white") self.assertEqual(result.body_type, "SEDAN") self.assertEqual(result.gearbox, "AT") self.assertEqual(result.drive, "FWD") self.assertEqual(result.origin, "OPENLANE") self.assertEqual(result.origin_id, "openlane:12345") self.assertEqual(result.origin_url, "https://app.openlane.com/vehicles/12345") self.assertEqual(result.evaluation, "1HGBH41JXMN109186") self.assertEqual(len(result.images), 2) self.assertEqual(result.images[0].fullres_image, "https://img.openlane.com/1_full.jpg") self.assertEqual(result.images[0].preview_image, "https://img.openlane.com/1_thumb.jpg") def test_missing_id_returns_none(self) -> None: record = {"make": "Toyota", "model": "Camry"} self.assertIsNone(map_openlane_record(record)) def test_missing_brand_returns_none(self) -> None: record = {"id": "123", "model": "Camry"} self.assertIsNone(map_openlane_record(record)) def test_missing_model_returns_none(self) -> None: record = {"id": "123", "make": "Toyota"} self.assertIsNone(map_openlane_record(record)) def test_vin_as_id_fallback(self) -> None: record = self._sample_record() del record["id"] record["vin"] = "WBAPH5C52BA012345" result = map_openlane_record(record) self.assertIsNotNone(result) self.assertEqual(result.origin_id, "openlane:WBAPH5C52BA012345") def test_existing_openlane_prefix_is_normalized(self) -> None: record = self._sample_record(id="openlane:777") result = map_openlane_record(record) self.assertIsNotNone(result) self.assertEqual(result.origin_id, "openlane:777") def test_whitespace_id_is_normalized(self) -> None: record = self._sample_record(id=" 888 ") result = map_openlane_record(record) self.assertIsNotNone(result) self.assertEqual(result.origin_id, "openlane:888") def test_image_url_strings(self) -> None: record = self._sample_record(images=["https://a.com/1.jpg", "https://a.com/2.jpg"]) result = map_openlane_record(record) self.assertEqual(len(result.images), 2) self.assertEqual(result.images[0].fullres_image, "https://a.com/1.jpg") def test_image_deduplication(self) -> None: record = self._sample_record(images=[ {"full": "https://a.com/same.jpg"}, {"full": "https://a.com/same.jpg"}, {"full": "https://a.com/other.jpg"}, ]) result = map_openlane_record(record) self.assertEqual(len(result.images), 2) def test_vehicle_images_api_format(self) -> None: record = self._sample_record(images=[ { "url": "https://a.com/original.jpg", "large_resolution_url": "https://a.com/large.jpg", "low_resolution_url": "https://a.com/medium.jpg", } ]) result = map_openlane_record(record) self.assertEqual(len(result.images), 1) self.assertEqual(result.images[0].fullres_image, "https://a.com/original.jpg") self.assertEqual(result.images[0].preview_image, "https://a.com/medium.jpg") def test_default_url_generation(self) -> None: record = self._sample_record() del record["url"] result = map_openlane_record(record) self.assertEqual(result.origin_url, "https://app.openlane.com/vehicles/12345") def test_nested_vehicle_fields(self) -> None: record = { "id": "999", "vehicle": { "make": "BMW", "model": "X5", "year": 2022, "mileage": 10000, }, } result = map_openlane_record(record) self.assertIsNotNone(result) self.assertEqual(result.brand, "BMW") self.assertEqual(result.model, "X5") self.assertEqual(result.year, 2022) def test_map_records_filters_invalid(self) -> None: records = [ self._sample_record(id="1"), {"no_id": True}, self._sample_record(id="2"), ] results = map_openlane_records(records) self.assertEqual(len(results), 2) def test_sale_type_mapping(self) -> None: record = self._sample_record(sale_type="buy_now") result = map_openlane_record(record) self.assertEqual(result.selling_type, "STOCK") record2 = self._sample_record(sale_type="tender") result2 = map_openlane_record(record2) self.assertEqual(result2.selling_type, "TENDER") class TestParseMileage(unittest.TestCase): def test_integer(self) -> None: self.assertEqual(_parse_mileage(45000), 45000) def test_string_with_commas(self) -> None: self.assertEqual(_parse_mileage("45,000 miles"), 45000) def test_none_returns_zero(self) -> None: self.assertEqual(_parse_mileage(None), 0) def test_float(self) -> None: self.assertEqual(_parse_mileage(45000.5), 45000) class TestParseEngineVolume(unittest.TestCase): def test_liters_float(self) -> None: self.assertEqual(_parse_engine_volume_cc(2.5), 2500) def test_cc_int(self) -> None: self.assertEqual(_parse_engine_volume_cc(2500), 2500) def test_string_liters(self) -> None: self.assertEqual(_parse_engine_volume_cc("2.5L"), 2500) def test_none(self) -> None: self.assertIsNone(_parse_engine_volume_cc(None)) class TestGenerateSlug(unittest.TestCase): def test_basic(self) -> None: slug = _generate_slug(2020, "TOYOTA", "CAMRY", "openlane:123") self.assertEqual(slug, "2020-toyota-camry-openlane-123") def test_no_year(self) -> None: slug = _generate_slug(None, "BMW", "X5", "openlane:456") self.assertEqual(slug, "bmw-x5-openlane-456") if __name__ == "__main__": unittest.main()