* test(tw): add live-network drift smoke + network-marked tests for 三大法人 fetcher
The offline tests (tests/test_tw_institutional_fetcher.py) pin the parser to
frozen fixtures, so they cannot notice an upstream TWSE T86 / TPEx feed change.
Add two additive, tw-only drift detectors that hit the real public endpoints:
- tests/tw_institutional_live_smoke.py: manual non-pytest smoke (mirrors
tests/longbridge_live_smoke.py). Checks endpoint liveness + core-column-name
presence, and cross-checks the fetcher's foreign/trust/dealer/total against
the raw columns plus the always-true reconstruction
total == foreign + foreign_dealer + trust + dealer (the 3-term identity only
holds when the foreign-dealer sub-component is 0).
- tests/test_tw_institutional_network.py: @pytest.mark.network, run only by the
non-blocking Network Smoke cron (pytest -m network); the blocking backend gate
runs pytest -m "not network" (scripts/ci_gate.sh) so these never gate a PR.
Two self-contained tests cross-check the fetcher against the raw feed.
No production code touched (data_provider/base.py unchanged); fail-open and
no-silent-pass preserved. A renamed core/foreign-dealer column or a non-JSON
response (maintenance page / URL migration) is reported LOUD as drift; only a
transport error or non-trading-day soft-skips. Verified live against today's
feeds and via a negative-path simulation of every drift/transient case.
Dual review (code-reviewer APPROVE + silent-failure-hunter PROCEED) caught and
fixed soft-skip paths that had masked feed drift (non-JSON body swallowed as a
blip; foreign-dealer rename fabricated via `or 0`).
* test(tw): fail loud when a stock is present in the raw feed but the fetcher returns None
Addresses the review correctness-blocker on #1855. The TWSE/TPEx drift tests
called the fetcher first and soft-skipped on a None result as "transient /
suspended" WITHOUT checking the raw feed — so an upstream parse-prerequisite
drift (e.g. a 民國->ISO date-format change that _parse_tpex_row / _parse_t86_row
cannot convert) would make get_institutional_net() fail-open to None and be
silently skipped, the exact fail-open these tests exist to catch (and contrary
to the PR's stated "民國->ISO date switch fails loud").
Reorder both the smoke and the network tests: resolve the target stock's raw
row FIRST, then if the fetcher returns None — fail LOUD when the row IS present
in the raw feed (parse/date drift), and only soft-skip when the row is genuinely
absent (non-trading day / suspended / transient).
Verified: a negative-path simulation (raw row present + an unconvertible payload
date) now reports drift and fails, the live happy path still passes, and the
network tests are stable across repeated runs (transient blips hit both the raw
fetch and the fetcher together -> consistent skip, never a false fail).
---------
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>