"""Phase 25 — invariant tripwires for parse_tuple_payload's fast-path dispatch. These tests don't exercise behavior. They lock down the structural invariants the optimized hot loop in :func:`informix_db._resultset.parse_tuple_payload` relies on for correctness. Each test is a CI tripwire — if a future contributor breaks an invariant, these fail at test time rather than at a customer's wire-protocol mismatch six months later. Lessons from Margaret Hamilton's review of Phases 23/24/25: * The optimization is *correct* — but its correctness depends on properties of unrelated tables (DECODERS keys, FIXED_WIDTHS keys, IfxType flag bits) staying consistent. * A comment at the table only helps if the next contributor reads it. * A test fails loudly the moment the invariant is broken. Prefer that. If one of these tests fires, **do not** simply update the test to match the new state — that defeats the purpose. Instead read the docstring on the failed test and the corresponding INVARIANT comment in the source; either restore the property or refactor the optimization to no longer depend on it. """ from __future__ import annotations from informix_db._resultset import ( _COMPOSITE_UDT_TYPES, _FIXED_WIDTH_TYPES, _LENGTH_PREFIXED_SHORT_TYPES, _NUMERIC_TYPES, _TC_DATETIME, _TC_INTERVAL, _TC_LVARCHAR, _TC_UDTFIXED, _TC_UDTVAR, ) from informix_db.converters import DECODERS, FIXED_WIDTHS def test_fixed_width_types_disjoint_from_other_dispatch_sets() -> None: """parse_tuple_payload's fast path is silently wrong if the FIXED_WIDTHS type set overlaps any other branch. The optimization in ``parse_tuple_payload`` puts the FIXED_WIDTHS branch FIRST. If a type is also in (e.g.) _NUMERIC_TYPES, the fast path swallows it before the DECIMAL/MONEY-specific handler runs — silently producing wrong values. If this test fails, you've added a new entry somewhere that overlaps. Either move it to FIXED_WIDTHS exclusively (and remove its specialized branch) or remove it from FIXED_WIDTHS. """ other_branch_types = ( _LENGTH_PREFIXED_SHORT_TYPES | _NUMERIC_TYPES | _COMPOSITE_UDT_TYPES | {_TC_LVARCHAR, _TC_DATETIME, _TC_INTERVAL, _TC_UDTFIXED, _TC_UDTVAR} ) overlap = _FIXED_WIDTH_TYPES & other_branch_types assert overlap == set(), ( f"FIXED_WIDTHS overlap with another parse_tuple_payload branch: {overlap}. " f"See the INVARIANT comment on FIXED_WIDTHS in converters.py." ) def test_every_fixed_width_type_has_a_decoder() -> None: """The fast path calls ``_decode_base(tc, raw, encoding)`` for every FIXED_WIDTHS key. If a key has no entry in DECODERS, we'd raise ``NotImplementedError`` for that column — surprising the user. If this test fails, you've added a key to FIXED_WIDTHS without adding a corresponding decoder. Add the decoder, or remove the key. """ missing = [tc for tc in FIXED_WIDTHS if tc not in DECODERS] assert missing == [], ( f"FIXED_WIDTHS has keys without DECODERS entries: {missing}. " f"Every fixed-width type must be decodable by _decode_base." ) def test_decoders_keys_stay_below_0x100() -> None: """The Phase 24 optimization in ``_decode_base`` skips ``base_type()`` by relying on a structural guarantee: all DECODERS keys are ≤ 0xFF and all flag bits in _types.py are ≥ 0x100, so a flagged type code cannot coincidentally match a DECODERS key. If this test fails, you've added a decoder for a type code with bits ≥ 0x100. The collision-free guarantee weakens — re-introduce ``base_type()`` inside ``_decode_base`` (and remove the Phase 24 optimization), OR keep the new key but verify it cannot clash with any flagged input. """ high_keys = [tc for tc in DECODERS if tc >= 0x100] assert high_keys == [], ( f"DECODERS contains keys with bits >= 0x100: {high_keys}. " f"See the INVARIANT comment on DECODERS in converters.py." )