From a0387425e190d728887cf681b14d8088f6818f17 Mon Sep 17 00:00:00 2001 From: agent_coder Date: Mon, 6 Jul 2026 18:55:51 +0300 Subject: [PATCH] =?UTF-8?q?test(render):=20=D1=8D=D1=82=D0=B0=D0=BF=205=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20=E2=80=94=20base-anchored=20fr?= =?UTF-8?q?agment=20oracle=20+=20=C2=A73.13=20boundary=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Golden-oracle DO: the fragment snapshot (media_fragments.json) was a circular self-snapshot generated from the refactored code. Regenerated it from the pre-refactor BASE post_parser.py (old ladders) so it holds base bytes — byte-identical to base for all 24 cases. The only 2 §3.14 deltas (file_unique_id_none, webpage_without_photo) moved to a new REGISTERED_DELTAS registry in media_fragment_replay.py; the fragment test asserts base bytes for 22 cases and the registered 5b value for 2. Docstrings corrected to describe base-anchoring (no overclaim); a WARNING in generate_snapshot() notes it must run against base code only. Test-coverage DO: added test_exactly_100mb_object_is_collected pinning the §3.13 guard as strictly '>' (exactly 100MB is still collected). Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/media_fragment_replay.py | 36 +++++++++++++++++++++++++--- tests/test_data/media_fragments.json | 4 ++-- tests/test_stage5_media_table.py | 13 ++++++---- tests/test_stage5b_media_fixes.py | 11 +++++++++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/tests/media_fragment_replay.py b/tests/media_fragment_replay.py index 3bf7c32..c018069 100644 --- a/tests/media_fragment_replay.py +++ b/tests/media_fragment_replay.py @@ -4,9 +4,15 @@ Captures the raw `_generate_html_media` output (BEFORE sanitize) for every media render kind and edge branch, as a SEPARATE oracle layer from the stage-0 feed goldens -(spec §4: "два слоя эталонов, не смешивать"). The 5a refactor (MEDIA_SOURCES table + -renderers) must reproduce these fragments byte-for-byte; 5b intentionally changes two -of them (§3.13 large-file guard is collection-only, §3.14 unclosed-div close). +(spec §4: "два слоя эталонов, не смешивать"). + +`media_fragments.json` is the PRE-REFACTOR BASE reference: it was captured by running +THIS harness against the BASE `post_parser.py` checkout (the pre-refactor code), NOT +against stage-5 code — so it is a genuine base-anchored golden layer, not a circular +self-snapshot of the refactor. The 5a refactor (MEDIA_SOURCES table + renderers) must +reproduce these base fragments byte-for-byte. The ONLY 5b-registered changes vs the base +are the two entries in REGISTERED_DELTAS (§3.14 unclosed-div close); the §3.13 large-file +guard is collection-only and changes no fragment bytes. Cases whose type exists in the recorded corpus could be pulled from it, but the media FRAGMENT is a pure function of a single Message, so deterministic hand-built mocks give @@ -125,6 +131,27 @@ def build_cases(): return cases +# --------------------------------------------------------------------------- # +# Registered 5b deltas vs the pre-refactor base snapshot. +# +# Spec §3.14: the empty
container is now CLOSED in every +# render branch. The base (pre-refactor) code left it OPEN whenever the selected media +# object had no usable file_unique_id, so the base snapshot captured an unbalanced div +# for exactly these two cases. `collected` is byte-identical to the base; only `html` +# differs by the added `
`. These are the ONLY fragments the 5b registered fixes +# change relative to the pre-refactor base — every other case reproduces base bytes. +REGISTERED_DELTAS = { + "file_unique_id_none": { + "collected": [], + "html": "
\n
", + }, + "webpage_without_photo": { + "collected": [], + "html": "
\n
\n
\n
\n\n
https://example.com
\n
\n
", + }, +} + + # --------------------------------------------------------------------------- # # Capture / compare. # --------------------------------------------------------------------------- # @@ -167,6 +194,9 @@ def _bootstrap_standalone(): def generate_snapshot(): + # WARNING: this MUST be run against the BASE (pre-refactor) `post_parser.py`, never + # against stage-5 code. Regenerating from stage-5 output would make the oracle a + # circular self-snapshot instead of a base-anchored golden layer (spec §4). from url_signer import KeyManager KeyManager.signing_key = FRAGMENT_SIGNING_KEY data = capture_fragments() diff --git a/tests/test_data/media_fragments.json b/tests/test_data/media_fragments.json index d62a926..cb0462d 100644 --- a/tests/test_data/media_fragments.json +++ b/tests/test_data/media_fragments.json @@ -65,7 +65,7 @@ }, "file_unique_id_none": { "collected": [], - "html": "
\n
" + "html": "
" }, "live_photo": { "collected": [ @@ -213,6 +213,6 @@ }, "webpage_without_photo": { "collected": [], - "html": "
\n
\n
\n
\n\n
https://example.com
\n
\n
" + "html": "
\n
\n
\n\n
https://example.com
\n
\n
" } } \ No newline at end of file diff --git a/tests/test_stage5_media_table.py b/tests/test_stage5_media_table.py index 0f0b9e0..f3b3bac 100644 --- a/tests/test_stage5_media_table.py +++ b/tests/test_stage5_media_table.py @@ -5,8 +5,10 @@ Two layers: * 5a — the MEDIA_SOURCES table + renderers reproduce the three old ladders - (_get_file_unique_id, _save_media_file_ids, _generate_html_media) byte-for-byte: - a fragment-level snapshot oracle (tests/test_data/media_fragments.json) plus + (_get_file_unique_id, _save_media_file_ids, _generate_html_media). The fragment + snapshot (tests/test_data/media_fragments.json) is the PRE-REFACTOR BASE reference, + captured against the base post_parser.py; the 5a code reproduces it byte-for-byte + (two fragments carry registered §3.14 deltas — see fr.REGISTERED_DELTAS). Plus table/selector/invariant unit tests. * 5b — the registered fixes (§3.13 large-file guard for every object, §3.14 the message-media div is closed in every branch) live in test_stage5b_media_fixes.py. @@ -54,8 +56,11 @@ def test_media_fragment_matches_snapshot(parser, case_name): parser._pending_media_ids = [] html = parser._generate_html_media(factory()) collected = [[c, p, f] for c, p, f, _ in parser._pending_media_ids] - assert html == snapshot[case_name]["html"], f"fragment HTML diverged for {case_name}" - assert collected == snapshot[case_name]["collected"], f"collection diverged for {case_name}" + # The snapshot is the pre-refactor BASE reference; the two §3.14 fragments carry a + # registered 5b delta (the now-closed message-media div), so expect their 5b bytes. + expected = fr.REGISTERED_DELTAS.get(case_name, snapshot[case_name]) + assert html == expected["html"], f"fragment HTML diverged for {case_name}" + assert collected == expected["collected"], f"collection diverged for {case_name}" def test_snapshot_covers_every_spec_case(): diff --git a/tests/test_stage5b_media_fixes.py b/tests/test_stage5b_media_fixes.py index c68f1a3..3d913b0 100644 --- a/tests/test_stage5b_media_fixes.py +++ b/tests/test_stage5b_media_fixes.py @@ -120,6 +120,17 @@ def test_small_non_video_object_still_collected(parser, media_type, attr): assert [(c, p, f) for c, p, f, _ in parser._pending_media_ids] == [("testchan", 11, "small")] +def test_exactly_100mb_object_is_collected(parser): + # §3.13 boundary: the guard is strictly `>` 100MB, so an object of exactly 100MB is + # still collected. Pins the operator — a mutation to `>=` would drop this and fail. + obj = SimpleNamespace(file_unique_id="edge", file_id="f", + file_size=100 * 1024 * 1024, mime_type="image/png") + msg = _msg(14, MessageMediaType.PHOTO, photo=obj) + parser._pending_media_ids = [] + parser._save_media_file_ids(msg) + assert [(c, p, f) for c, p, f, _ in parser._pending_media_ids] == [("testchan", 14, "edge")] + + def test_large_video_still_not_collected(parser): # Regression: the video case (the only one guarded before 5b) still works. msg = _msg(12, MessageMediaType.VIDEO,