oem_crawler/tests/test_audi_parser.py
2026-11-07 16:53:30 +01:00

325 lines
13 KiB
Python

"""
Unit tests for the Audi Stock Car Service parser and normalization logic.
"""
from datetime import date
from decimal import Decimal
import pytest
from oem_crawler.crawlers.base import CrawlerRegistry
from oem_crawler.crawlers.audi.parser import (
map_audi_drivetrain,
map_audi_fuel_type,
map_audi_series,
map_audi_transmission,
parse_audi_vehicle,
parse_german_date,
parse_power,
)
from oem_crawler.db.schemas import (
BatteryCapacityUnit,
DrivetrainType,
FuelType,
TransmissionType,
VehicleCondition,
VehicleStatus,
)
class TestAudiParser:
def test_registry_contains_audi(self):
assert "audi" in CrawlerRegistry.supported_brands()
crawler_cls = CrawlerRegistry.get("audi")
crawler = crawler_cls(market_country="DE")
assert crawler.brand_name == "AUDI"
assert crawler.get_market_code("new") == "de"
assert crawler.get_market_code("used") == "deuc"
def test_map_fuel_type(self):
assert map_audi_fuel_type({"code": "E", "description": "Elektro"}) == FuelType.BEV
assert map_audi_fuel_type("Elektro") == FuelType.BEV
assert map_audi_fuel_type({"code": "H", "description": "Hybrid (Benzin/Elektro)"}) == FuelType.PHEV
assert map_audi_fuel_type({"code": "B", "description": "Benzin"}) == FuelType.PETROL
assert map_audi_fuel_type({"code": "D", "description": "Diesel"}) == FuelType.DIESEL
assert map_audi_fuel_type("Mild-Hybrid") == FuelType.MHEV
assert map_audi_fuel_type("g-tron Erdgas") == FuelType.CNG
def test_map_transmission(self):
assert map_audi_transmission({"code": "s-tronic", "description": "S tronic"}) == TransmissionType.AUTOMATIC
assert map_audi_transmission("tiptronic") == TransmissionType.AUTOMATIC
assert map_audi_transmission("Automatik") == TransmissionType.AUTOMATIC
assert map_audi_transmission({"code": "manual", "description": "Handschaltung"}) == TransmissionType.MANUAL
assert map_audi_transmission("Schaltgetriebe") == TransmissionType.MANUAL
assert map_audi_transmission(None) is None
def test_map_drivetrain(self):
assert map_audi_drivetrain({"code": "quattro", "description": "quattro"}) == DrivetrainType.AWD
assert map_audi_drivetrain("Allradantrieb") == DrivetrainType.AWD
assert map_audi_drivetrain({"code": "fwd", "description": "Frontantrieb"}) == DrivetrainType.FWD
assert map_audi_drivetrain("Heckantrieb") == DrivetrainType.RWD
assert map_audi_drivetrain(None) is None
def test_map_series(self):
assert map_audi_series({"description": "A3"}) == "A3"
assert map_audi_series({"description": "Q4 e-tron"}) == "Q4 e-tron"
assert map_audi_series(None, "Audi A1 Sportback 25 TFSI") == "A1"
assert map_audi_series(None, "Audi e-tron GT quattro 350 kW") == "e-tron GT"
assert map_audi_series(None, "Audi RS6 Avant performance TFSI") == "RS6"
assert map_audi_series(None, "Audi Q8 50 TDI quattro") == "Q8"
def test_parse_power(self):
kw, hp = parse_power("110 kW (150 PS)")
assert kw == 110
assert hp == 150
kw2, hp2 = parse_power("70 kW")
assert kw2 == 70
assert hp2 == 95
kw3, hp3 = parse_power(None)
assert kw3 is None
assert hp3 is None
def test_parse_german_date(self):
assert parse_german_date("21.07.2015") == date(2015, 7, 21)
assert parse_german_date("2021-10-20") == date(2021, 10, 20)
assert parse_german_date(1319068800000) == date(2011, 10, 20)
assert parse_german_date(None) is None
def test_parse_audi_new_ice_vehicle(self):
basic_item = {
"id": "REVVMjA2NjUxNzdBQlNXMzQyMDI1=",
"carId": "DEU20665177ABSW342025",
"decodedCarId": "DEU20665177ABSW342025",
"commissionNumber": "177ABSW342025",
"model": {
"code": "aacv",
"description": "Audi A1 Sportback 25 TFSI 70 kW (95 PS) Schaltgetriebe",
},
"symbolicCarlineGroup": {
"description": "A1",
},
"bodyType": {
"description": "Sportback",
},
"driveType": {
"code": "drive.fwd",
"description": "Frontantrieb",
},
"gearType": {
"code": "gear-type.manual",
"description": "Handschaltung",
},
"fuel": {
"code": "B",
"description": "Benzin",
},
"powerDisplay": "70 kW (95 PS)",
"modelYear": 2025,
"typedPrices": [
{"amount": 356.0, "currencyCode": "EUR", "type": "financeRate"},
{"amount": 21480.0, "currencyCode": "EUR", "type": "retail"},
],
"dealer": {
"id": "20665",
"name": "Autohaus Schnitzler GmbH & Co. KG",
"street": "Westring 20-22",
"zipCode": "40721",
"city": "Hilden",
"geoLocation": {
"lat": 51.17704,
"lon": 6.91641,
},
},
"used": {},
"extColor": {"description": "Gletscherweiß Metallic", "code": "2Y2Y"},
"intColor": {"description": "Schwarz"},
"tilesPictures": ["https://mediaservice.audi.com/media/tile.jpg"],
"pictures": ["https://mediaservice.audi.com/media/full.jpg"],
}
v = parse_audi_vehicle(basic_item=basic_item, market_country="DE", requested_stock_type="new")
assert v.brand == "AUDI"
assert v.oem_vehicle_id == "DEU20665177ABSW342025"
assert v.series == "A1"
assert v.model_name == "Audi A1 Sportback 25 TFSI 70 kW (95 PS) Schaltgetriebe"
assert v.body_type == "Sportback"
assert v.condition == VehicleCondition.NEW
assert v.fuel_type == FuelType.PETROL
assert v.transmission == TransmissionType.MANUAL
assert v.drivetrain == DrivetrainType.FWD
assert v.power_kw == 70
assert v.power_hp == 95
assert v.model_year == 2025
assert v.mileage_km == 0
assert v.price_gross == Decimal("21480.0")
assert v.monthly_rate == Decimal("356.0")
assert v.dealer_name == "Autohaus Schnitzler GmbH & Co. KG"
assert v.dealer_id == "20665"
assert v.location_city == "Hilden"
assert v.location_postal_code == "40721"
assert v.latitude == Decimal("51.17704")
assert v.longitude == Decimal("6.91641")
assert v.exterior_color == "Gletscherweiß Metallic"
assert v.status == VehicleStatus.IN_STOCK
assert "REVVMjA2NjUxNzdBQlNXMzQyMDI1=" in v.vehicle_url
assert len(v.image_urls) == 2
def test_parse_audi_used_vehicle(self):
basic_item = {
"id": "REVVMjIyNjNHVzE4ODMw=",
"carId": "DEU22263GW18830",
"model": {
"description": "Audi A3 Sportback 1.4 TFSI attraction",
},
"symbolicCarlineGroup": {
"description": "A3",
},
"fuel": {
"code": "B",
"description": "Benzin",
},
"gearType": {
"description": "Handschaltung",
},
"driveType": {
"description": "Frontantrieb",
},
"powerDisplay": "92 kW (125 PS)",
"typedPrices": [
{"amount": 10890.0, "currencyCode": "EUR", "type": "retail"}
],
"dealer": {
"name": "Autohaus Pietsch.Bünde GmbH",
"city": "Bünde",
"zipCode": "32257",
"geoLocation": {"lat": 52.2025, "lon": 8.57877},
},
"warrantyPlus": True,
"used": {
"initialRegistration": "21.07.2015",
"mileage": 108859.0,
"numPreviousOwners": 2,
"pictureUrls": ["https://vtpimages.audi.com/carimg2/used.jpg"],
},
}
v = parse_audi_vehicle(basic_item=basic_item, market_country="DE", requested_stock_type="used")
assert v.brand == "AUDI"
assert v.series == "A3"
assert v.condition == VehicleCondition.CERTIFIED_PRE_OWNED # warrantyPlus is True
assert v.mileage_km == 108859
assert v.first_registration_date == date(2015, 7, 21)
assert v.previous_owners_count == 2
assert v.price_gross == Decimal("10890.0")
assert v.latitude == Decimal("52.2025")
assert v.longitude == Decimal("8.57877")
assert "gebrauchtwagenboerse" in v.vehicle_url
def test_parse_audi_ev_with_detail(self):
basic_item = {
"id": "REVVNDcyNTIxNzdaWlpZNDMyMDI2=",
"carId": "DEU47252177ZZZY432026",
"model": {
"description": "Audi Q4 Sportback e-tron 150 kW",
},
"symbolicCarlineGroup": {
"description": "Q4 e-tron",
},
"fuel": {
"code": "E",
"description": "Elektro",
},
"gearType": {
"description": "Automatik",
},
"powerDisplay": "150 kW (204 PS)",
"typedPrices": [
{"amount": 48900.0, "currencyCode": "EUR", "type": "retail"}
],
"dealer": {
"name": "Audi Zentrum Hamburg",
"city": "Hamburg",
"zipCode": "22525",
"geoLocation": {"lat": 53.58, "lon": 9.92},
},
}
detail_data = {
"vehicle": {
"detail": {
"techData": {
"fields": [
{"name": "Netto-Batterie-Kapazität", "value": "59 kWh"},
{"name": "Brutto-Batterie-Kapazität", "value": "63 kWh"},
{"name": "Max. Ladeleistung AC / HPC", "value": "11 kW / 160 kW"},
{"name": "Ladedauer DC 10-80% bei max. Ladeleistung", "value": "27 min"},
{"name": "Elektrische Reichweite kombiniert (WLTP)", "value": "415 km"},
]
},
"features": [
{
"key": "5SL",
"prno": "MASL5SL",
"name": "Außenspiegel links, asphärisch",
"groupName": "Außenausstattung",
"kind": "serie",
},
{
"key": "KA2",
"prno": "MKA2KA2",
"name": "Rückfahrkamera",
"groupName": "Assistenzsysteme",
"kind": "sonder",
},
],
}
}
}
v = parse_audi_vehicle(
basic_item=basic_item,
detail_data=detail_data,
market_country="DE",
requested_stock_type="new",
)
assert v.brand == "AUDI"
assert v.series == "Q4 e-tron"
assert v.fuel_type == FuelType.BEV
assert v.battery_capacity == Decimal("59")
assert v.battery_capacity_net_kwh == Decimal("59")
assert v.battery_capacity_gross_kwh == Decimal("63")
assert v.battery_capacity_unit == BatteryCapacityUnit.KWH
assert v.charging_power_ac_kw == Decimal("11")
assert v.charging_power_dc_kw == Decimal("160")
assert v.charging_time_dc_minutes == 27
assert v.electric_range_wltp_km == 415
# Check options
assert len(v.options) == 2
assert v.options[0].option_code == "5SL"
assert v.options[0].is_standard is True
assert v.options[1].option_code == "KA2"
assert v.options[1].is_standard is False
assert v.options[1].category == "Assistenzsysteme"
def test_fetch_search_page_pagination_params(self):
from unittest.mock import MagicMock
from oem_crawler.crawlers.audi.crawler import AudiStockCrawler
crawler = AudiStockCrawler(market_country="DE")
mock_client = MagicMock()
mock_resp = MagicMock()
mock_resp.status_code = 200
mock_resp.json.return_value = {"totalCount": 100, "vehicleBasic": []}
mock_client.get.return_value = mock_resp
crawler.fetch_search_page(mock_client, market_code="de", page=2, size=50)
mock_client.get.assert_called_once()
call_kwargs = mock_client.get.call_args.kwargs
assert call_kwargs["params"]["from"] == 100
assert call_kwargs["params"]["size"] == 50