Model/backend/documents_parser/tests
Khalim Conn-Kowlessar b7fbbcca96 Slice S0380.51: strict-raise UnmappedApiCode on API integer enums
Mirrors the Elmhurst `UnmappedElmhurstLabel` coverage gate on the
GOV.UK API path. The same failure mode (silently routing an unknown
enum to a default / None hides cascade gaps until a downstream SAP-
delta investigation surfaces them) was hitting the API mapper:
existing helpers like `_api_floor_construction_str` returned None on
unrecognised codes per the comment "Only the values observed across
the 10 golden fixtures (1, 2) are mapped; unrecognised codes fall
through to None."

Adds `UnmappedApiCode(ValueError)` at the API mapper boundary and
threads it through five strict helpers:

- `_api_party_wall_construction_int`     (RdSAP10 Table 15)
- `_api_floor_construction_str`          (Slice 88 floor signal)
- `_api_floor_type_str`                  (RdSAP10 §5 rule (12))
- `_api_roof_construction_str`           (Slice 89 cos(30°) factor)
- `_api_sheltered_sides`                 (SAP10.2 §S5)

Each helper distinguishes:
- "lodging absent" → return None (unchanged behaviour)
- "lodging present and mapped" → translate (unchanged behaviour)
- "lodging present but unrecognised" → raise UnmappedApiCode (NEW)

Two coverage gaps surfaced immediately at strict-run, both fixed
in the same slice with the worksheet-backed lodged-floor descriptions:

1. `floor_heat_loss=2` — cert 7536 Main lodges this (floors[]
   description "To unheated space, insulated"); also lodged on cert
   2031 / etc. Added mapping → "To unheated space".
2. `floor_heat_loss=3` — cert 7536 Ext2 lodges this with the same
   floors[] description as Main code 2 — same cascade signal.
3. `floor_heat_loss=6` — cert 9501 + cert 9390 (top-floor flats)
   lodge this with floors[] description "(another dwelling below)".
   The cascade routes party-floor handling via property_type=Flat +
   cert.floors[] description independently of this string, so the
   explicit None entry preserves the cascade match (cert 9501 stays
   at exact 1e-4 SAP vs worksheet 68.5252) while distinguishing
   "decided no string" from "unknown".

Six new tests document the contract:
- Five unit tests inject an out-of-range integer (99) into a real
  cohort cert JSON and assert UnmappedApiCode raises with the right
  `field` and `value`.
- One coverage forcing function (`test_all_golden_fixtures_extract
  _via_api_without_unmapped_code_raise`) loops every JSON under
  `fixtures/golden/` through `from_api_response` and asserts no
  raise — future fixtures with unmapped enums fail this test until
  a dict entry is added.

763 → 769 pass + 0 fail (5 unit + 1 cohort-coverage test added).
Pyright net-zero (32 → 32 baseline preserved).

The pattern is ready to extend to other silently-falling-through
helpers — e.g., `_api_glazing_transmission` (codes 4-12, 15+ noted
in the existing comment as "not yet mapped — incremental coverage
as new fixtures surface them"), `_api_cascade_glazing_type` (pass-
through is intentional, so probably leave alone). Each addition
is its own slice.
2026-05-28 20:34:15 +00:00
..
fixtures Slice S0380.19: count Elmhurst shower outlets by type (no more hardcoded 1) 2026-05-28 07:16:32 +00:00
__init__.py Map to RdSapSiteNotes from site notes JSON 🟥 2026-04-16 13:54:03 +00:00
test_elmhurst_end_to_end.py Slice S0380.17: map Elmhurst §11 glazing-type labels to SAP10 codes 2026-05-27 23:05:52 +00:00
test_elmhurst_extractor.py extract window frame details from elmhurst site notes 🟥 2026-04-27 15:50:25 +00:00
test_end_to_end.py P6.1 follow-on: unbox BuildingPartIdentifier at backend boundaries 2026-05-20 09:58:23 +00:00
test_extractor.py Handle wall thickness "Unmeasurable" 🟩 2026-04-30 16:41:16 +00:00
test_pdf.py rename example site notes to PasHub_ and add Elmhurst example 2026-04-24 13:01:51 +00:00
test_summary_pdf_mapper_chain.py Slice S0380.51: strict-raise UnmappedApiCode on API integer enums 2026-05-28 20:34:15 +00:00