199 lines
7.3 KiB
Python
199 lines
7.3 KiB
Python
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()
|