186 lines
7.0 KiB
Python
186 lines
7.0 KiB
Python
import unittest
|
||
from unittest.mock import MagicMock
|
||
|
||
from encar_scraper.encar import EncarMapper, EncarScraper, ENCAR_DETAIL_URL_TEMPLATE
|
||
|
||
|
||
class TestEncarMapper(unittest.TestCase):
|
||
def test_map_to_car_record(self):
|
||
payload = {
|
||
"Id": 41421262,
|
||
"Manufacturer": "Hyundai",
|
||
"Model": "G90 (RS4)",
|
||
"FormYear": "2023",
|
||
"Price": 8590,
|
||
"Mileage": 37923,
|
||
"Photos": [
|
||
{"location": "/carpicture01/pic4141/41412663_001.jpg", "ordering": 1},
|
||
{"location": "/carpicture01/pic4141/41412663_003.jpg", "ordering": 3},
|
||
],
|
||
}
|
||
|
||
record = EncarMapper().map_to_car_record(
|
||
ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id="41421262"),
|
||
payload,
|
||
)
|
||
|
||
self.assertEqual(record.origin_id, "encar:41421262")
|
||
self.assertEqual(record.parser_id, "encar:41421262")
|
||
self.assertEqual(record.brand, "Hyundai")
|
||
self.assertEqual(record.model, "G90")
|
||
self.assertEqual(record.year, 2023)
|
||
self.assertEqual(record.price, 85900000) # 8590 만원 × 10000
|
||
self.assertEqual(record.mileage, 37923)
|
||
self.assertEqual(record.country, "KR")
|
||
self.assertEqual(record.currency, "KRW")
|
||
self.assertEqual(len(record.images), 2)
|
||
self.assertTrue(record.images[0].fullres_image.endswith("_001.jpg"))
|
||
|
||
def test_extract_vehicle_id_from_url(self):
|
||
url = ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id="41421262")
|
||
self.assertEqual(EncarMapper()._extract_vehicle_id(url), "41421262")
|
||
|
||
def test_map_to_car_record_uses_base_model_without_trim(self):
|
||
payload = {
|
||
"Id": 41854374,
|
||
"Manufacturer": "현대",
|
||
"Model": "Genesis",
|
||
"Badge": "BH330 Grand",
|
||
"BadgeDetail": "Prime팩",
|
||
"FormYear": "2011",
|
||
"Price": 1200,
|
||
"Mileage": 100000,
|
||
"Photos": [
|
||
{"location": "/carpicture01/pic4185/41854374_001.jpg", "ordering": 1},
|
||
],
|
||
}
|
||
|
||
record = EncarMapper().map_to_car_record(
|
||
ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id="41854374"),
|
||
payload,
|
||
)
|
||
|
||
self.assertEqual(record.brand, "Hyundai")
|
||
self.assertEqual(record.model, "Genesis")
|
||
self.assertEqual(record.origin_id, "encar:41854374")
|
||
|
||
def test_map_to_car_record_resolves_numeric_id_from_photos(self):
|
||
payload = {
|
||
"Id": "car-BOqLLkjkgbpStlVJEJvbRG",
|
||
"Manufacturer": "Kia",
|
||
"Model": "K3",
|
||
"FormYear": "2018",
|
||
"Price": 990,
|
||
"Mileage": 75000,
|
||
"Photos": [
|
||
{"location": "/carpicture01/pic4187/41878507_001.jpg", "ordering": 1},
|
||
],
|
||
}
|
||
|
||
record = EncarMapper().map_to_car_record("", payload)
|
||
|
||
self.assertEqual(record.origin_id, "encar:41878507")
|
||
self.assertEqual(record.parser_id, "encar:41878507")
|
||
self.assertIn("carid=41878507", record.origin_url)
|
||
|
||
def test_translate_model_keeps_english_text(self):
|
||
mapper = EncarMapper()
|
||
|
||
self.assertEqual(mapper._translate_model("All New Carnival"), "Carnival")
|
||
self.assertEqual(mapper._translate_model("New Kia Ray"), "Kia Ray")
|
||
self.assertEqual(mapper._translate_model("K5 Hybrid 3rd gen"), "K5 Hybrid")
|
||
self.assertEqual(mapper._translate_model("New K5 3rd gen"), "K5")
|
||
self.assertEqual(mapper._translate_model("New Tiguan"), "Tiguan")
|
||
self.assertEqual(mapper._translate_model("New QM6"), "QM6")
|
||
self.assertEqual(mapper._translate_model("Discovery Sport 2nd gen"), "Discovery Sport")
|
||
|
||
def test_translate_model_handles_korean_fragments(self):
|
||
mapper = EncarMapper()
|
||
|
||
cases = {
|
||
"5시리즈 (G30)": "5 Series",
|
||
"S-클래스 W223": "S-Class W223",
|
||
"Sonata 디 엣지(DN8)": "Sonata The Edge",
|
||
"렉스턴 Sport 칸": "Rexton Sport Khan",
|
||
"티볼리 Air": "Tivoli Air",
|
||
"코란도 투리스모": "Korando Turismo",
|
||
"Kia Carnival 4세대": "Kia Carnival",
|
||
"Cadillac 에스컬레이드 5세대": "Cadillac Escalade",
|
||
"K7 프리미어": "K7 Premier",
|
||
}
|
||
|
||
for raw, expected in cases.items():
|
||
with self.subTest(raw=raw):
|
||
self.assertEqual(mapper._translate_model(raw), expected)
|
||
|
||
def test_translate_model_drops_untranslated_korean(self):
|
||
mapper = EncarMapper()
|
||
|
||
cases = {
|
||
"슈팅브레이크": "",
|
||
"The New G70 슈팅브레이크": "G70",
|
||
"G70 (슈팅브레이크)": "G70",
|
||
}
|
||
|
||
for raw, expected in cases.items():
|
||
with self.subTest(raw=raw):
|
||
self.assertEqual(mapper._translate_model(raw), expected)
|
||
|
||
def test_translate_color_normalizes_api_spacing(self):
|
||
mapper = EncarMapper()
|
||
|
||
self.assertEqual(mapper._translate_color("흰색"), "white")
|
||
self.assertEqual(mapper._translate_color("흰 색"), "white")
|
||
self.assertEqual(mapper._translate_color("검정색"), "black")
|
||
self.assertEqual(mapper._translate_color("진주색"), "pearl")
|
||
self.assertEqual(mapper._translate_color("쥐색"), "mouse gray")
|
||
self.assertEqual(mapper._translate_color("은회색"), "silver-gray")
|
||
self.assertEqual(mapper._translate_color("하늘색"), "sky blue")
|
||
self.assertEqual(mapper._translate_color("자주색"), "burgundy")
|
||
|
||
def test_apply_batch_color_uses_spec_color_name(self):
|
||
payload = {
|
||
"Id": 42456458,
|
||
"Manufacturer": "Lincoln",
|
||
"Model": "Corsair",
|
||
"FormYear": "2022",
|
||
"Price": 2690,
|
||
"Mileage": 26600,
|
||
}
|
||
|
||
mapper = EncarMapper()
|
||
record = mapper.map_to_car_record(
|
||
ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id="42456458"),
|
||
payload,
|
||
)
|
||
mapper.apply_batch_color(record, {"spec": {"colorName": "흰 색"}})
|
||
|
||
self.assertEqual(record.brand, "Lincoln")
|
||
self.assertEqual(record.model, "Corsair")
|
||
self.assertEqual(record.year, 2022)
|
||
self.assertEqual(record.price, 26900000)
|
||
self.assertEqual(record.mileage, 26600)
|
||
self.assertEqual(record.color, "white")
|
||
|
||
|
||
class TestEncarScraper(unittest.TestCase):
|
||
def test_collect_listing_limits_results(self):
|
||
scraper = EncarScraper()
|
||
|
||
scraper._fetch_listing_page = MagicMock(side_effect=[
|
||
{"SearchResults": [{"Id": 1}, {"Id": 2}]},
|
||
])
|
||
|
||
result = scraper.collect_listing(limit=1, page_size=2)
|
||
self.assertEqual(result["items_collected"], 1)
|
||
self.assertEqual(result["vehicle_urls"], [ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id="1")])
|
||
|
||
def test_scrape_vehicle_detail_raises_without_id(self):
|
||
scraper = EncarScraper()
|
||
with self.assertRaises(ValueError):
|
||
scraper.scrape_vehicle_detail("https://www.encar.com/invalid")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|