Code-reviewer follow-up on the parity harness (aaaf8179):
- parity.py: plain `==` in the scalar-equality branch let `True == 1` /
`False == 0` silently pass (Python bool is an int subclass). A future
migration bug turning a `bool | None` field into a raw 0/1 would slip
through undetected. Now any type mismatch where exactly one side is a
bool is reported as a diff, regardless of numeric equality.
- test_parity.py: unit test for the new bool-vs-int branch.
- test_avito_detail_kit_parity.py: the existing smoke test only proved the
harness reports "no diff" on two genuinely-identical real dataclass
instances — it never proved the harness catches a real divergence on
this same 30+-field shape (only the toy fixtures in test_parity.py did).
Added a test that mutates `price_rub` via dataclasses.replace() on the
real kit DetailEnrichment and asserts assert_parity raises
ParityMismatchError naming that field.
Stage 0 of the scraper_kit migration epic (#2277): shared test tool for
issues #2305-#2310, which each need to prove their kit-path importer
produces the same output as the legacy path on the same input.
- tests/support/parity.py: assert_parity()/compare_outputs() normalize
dataclass/pydantic outputs to dict/list/scalar before comparing, since
legacy vs kit dataclasses (e.g. DetailEnrichment) are different classes
and dataclass __eq__ always returns False across classes even when all
field values match. Supports ignore_fields (drop non-deterministic
fields like latency_ms/fetched_at) and numeric tolerance (math.isclose)
for float fields, with an assertion listing every differing field
(path + legacy value + kit value) on mismatch.
- tests/support/test_parity.py: unit tests for the harness itself
(identical outputs pass, differing outputs raise with informative
diff, tolerance/ignore_fields options, cross-class dataclass parity).
- tests/scrapers/test_avito_detail_kit_parity.py: end-to-end smoke proof
against real code — app.services.scrapers.avito_detail.parse_detail_html
(legacy, reached via admin.py's scrape_avito_detail debug endpoint
through fetch_detail) vs scraper_kit.providers.avito.detail's copy, on
a fixed HTML fixture.
- tests/support/README.md: usage note for #2305-#2310 migration PRs.
Found while implementing: tests/test_scraper_kit_*_parity.py (9 files,
~3400 lines) already do ad-hoc `dataclasses.asdict(old) == asdict(new)`
parity checks for the already-migrated SERP scraper modules (avito/cian/
domclick/yandex/base/scheduler/pipeline) — this harness generalizes that
repeated pattern for the remaining 12 non-scraper importers, adding
ignore_fields/tolerance which those ad-hoc checks don't have.