Prepare mobile de parser preview
This commit is contained in:
141
tests/test_fast_sync.py
Normal file
141
tests/test_fast_sync.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from iaai_scraper.browser.fast_client import FastListingVehicle
|
||||
from iaai_scraper.core.config import Settings
|
||||
from iaai_scraper.core.runtime_config import RuntimeConfig, RuntimeFiltersConfig
|
||||
from iaai_scraper.fast_sync import FastSyncEngine
|
||||
from iaai_scraper.parsing.fast_mapper import FastCarMapper
|
||||
|
||||
|
||||
def _listing_vehicle(inventory_id: str, *, status: str = "RS") -> FastListingVehicle:
|
||||
return FastListingVehicle(
|
||||
inventory_id=inventory_id,
|
||||
tenant="US",
|
||||
auction_id="1_1",
|
||||
auction_date="2026-04-08T08:30:00+00:00",
|
||||
inventory_status=status,
|
||||
currency="USD",
|
||||
timed_auction_closed=False,
|
||||
timed_auction_indicator=False,
|
||||
prebid_indicator=True,
|
||||
buynow_indicator=False,
|
||||
)
|
||||
|
||||
|
||||
def _detail_payload(inventory_id: str, *, make: str = "ACURA", model: str = "RSX", bid: int = 500) -> dict:
|
||||
return {
|
||||
"inventoryView": {
|
||||
"attributes": {
|
||||
"Id": inventory_id,
|
||||
"Year": "2002",
|
||||
"Make": make,
|
||||
"Model": model,
|
||||
"Series": "BASE",
|
||||
"Currency": "USD",
|
||||
"ODOValue": "219187",
|
||||
"ExteriorColor": "GRAY",
|
||||
"DriveLineTypeDesc": "FWD",
|
||||
"Transmission": "Automatic",
|
||||
"BodyStyleName": "COUPE",
|
||||
"EngineSize": "2.0L I-4",
|
||||
"VehicleGrade": "50",
|
||||
"PrimaryDamageDesc": "NORMAL WEAR & TEAR",
|
||||
},
|
||||
"imageDimensions": {
|
||||
"keys": {"$values": [{"k": f"{inventory_id}~I1", "w": 1600, "h": 1200, "i": 0}]}
|
||||
},
|
||||
},
|
||||
"auctionInformation": {
|
||||
"prebidInformation": {"decimalHighBidAmount": str(bid), "highBidAmount": f"${bid}"},
|
||||
"biddingInformation": {"buyNowPrice": "$0"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class FakeFastClient:
|
||||
def __init__(self, listing: list[FastListingVehicle], details: dict[str, dict]) -> None:
|
||||
self.listing = listing
|
||||
self.details = details
|
||||
self.detail_calls = 0
|
||||
self.persist_calls = 0
|
||||
|
||||
def iter_listing_vehicles(self, *, listing_start_url=None, make=None, max_pages=None): # noqa: ANN001, ANN202
|
||||
del listing_start_url, make, max_pages
|
||||
return iter(self.listing)
|
||||
|
||||
def fetch_vehicle_detail_payload(self, inventory_id: str) -> dict:
|
||||
self.detail_calls += 1
|
||||
return self.details[inventory_id]
|
||||
|
||||
def persist_session_state(self) -> None:
|
||||
self.persist_calls += 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class FakePersistence:
|
||||
inserted: int = 0
|
||||
images: int = 0
|
||||
|
||||
def get_existing_urls_and_ids(self, origin_urls, origin_ids): # noqa: ANN001, ANN202
|
||||
del origin_urls, origin_ids
|
||||
return set(), set()
|
||||
|
||||
def upsert_cars_batch(self, records): # noqa: ANN001, ANN202
|
||||
self.inserted += len(records)
|
||||
self.images += sum(len(record.images) for record in records)
|
||||
return {"inserted": len(records), "updated": 0, "images_upserted": sum(len(record.images) for record in records)}
|
||||
|
||||
def mark_sold_not_in_listing_by_urls(self, active_origin_urls, lane="iaai"): # noqa: ANN001, ANN202
|
||||
del active_origin_urls, lane
|
||||
return 0
|
||||
|
||||
|
||||
def test_fast_sync_engine_collects_details_and_bulk_upserts() -> None:
|
||||
listing = [_listing_vehicle("45078011~US"), _listing_vehicle("45268167~US")]
|
||||
details = {
|
||||
"45078011~US": _detail_payload("45078011~US", make="ACURA", model="RSX", bid=500),
|
||||
"45268167~US": _detail_payload("45268167~US", make="BMW", model="X5", bid=900),
|
||||
}
|
||||
client = FakeFastClient(listing, details)
|
||||
persistence = FakePersistence()
|
||||
engine = FastSyncEngine(
|
||||
client=client, # type: ignore[arg-type]
|
||||
mapper=FastCarMapper(),
|
||||
persistence=persistence, # type: ignore[arg-type]
|
||||
batch_size=10,
|
||||
fetch_concurrency=2,
|
||||
)
|
||||
|
||||
result = engine.sync_listing(runtime_config=RuntimeConfig(), only_new=False)
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert result["cars_upserted"] == 2
|
||||
assert result["images_upserted"] == 2
|
||||
assert result["listing"]["mode"] == "fast_hidden_payload"
|
||||
assert client.detail_calls == 2
|
||||
assert client.persist_calls == 1
|
||||
|
||||
|
||||
def test_fast_sync_engine_applies_runtime_filters_before_db() -> None:
|
||||
listing = [_listing_vehicle("45078011~US"), _listing_vehicle("45268167~US")]
|
||||
details = {
|
||||
"45078011~US": _detail_payload("45078011~US", make="ACURA", model="RSX", bid=500),
|
||||
"45268167~US": _detail_payload("45268167~US", make="BMW", model="X5", bid=900),
|
||||
}
|
||||
runtime = RuntimeConfig(filters=RuntimeFiltersConfig.from_dict({"brands": ["BMW"]}))
|
||||
persistence = FakePersistence()
|
||||
engine = FastSyncEngine(
|
||||
client=FakeFastClient(listing, details), # type: ignore[arg-type]
|
||||
mapper=FastCarMapper(),
|
||||
persistence=persistence, # type: ignore[arg-type]
|
||||
batch_size=10,
|
||||
fetch_concurrency=2,
|
||||
)
|
||||
|
||||
result = engine.sync_listing(runtime_config=runtime, only_new=False)
|
||||
|
||||
assert result["cars_upserted"] == 1
|
||||
assert result["cars_filtered"] == 1
|
||||
assert persistence.inserted == 1
|
||||
Reference in New Issue
Block a user