test(render): этап 5 ревью — base-anchored fragment oracle + §3.13 boundary (#32)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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 <div class="message-media"> 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 `</div>`. 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": "<div class=\"message-media\">\n</div>",
|
||||
},
|
||||
"webpage_without_photo": {
|
||||
"collected": [],
|
||||
"html": "<div class=\"message-media\">\n</div>\n<div class=\"webpage-preview\">\n<div class=\"webpage-preview\" style=\"border-left: 3px solid #ccc; padding-left: 10px; margin: 10px 0;\">\n<div class=\"webpage-title\" style=\"font-weight:bold; margin:5px 0;\">\n<a href=\"https://example.com\" target=\"_blank\">Example</a></div>\n<div class=\"webpage-url\" style=\"color:#666; font-size:0.9em;margin-bottom:5px;\">https://example.com</div>\n</div>\n</div>",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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()
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
},
|
||||
"file_unique_id_none": {
|
||||
"collected": [],
|
||||
"html": "<div class=\"message-media\">\n</div>"
|
||||
"html": "<div class=\"message-media\">"
|
||||
},
|
||||
"live_photo": {
|
||||
"collected": [
|
||||
@@ -213,6 +213,6 @@
|
||||
},
|
||||
"webpage_without_photo": {
|
||||
"collected": [],
|
||||
"html": "<div class=\"message-media\">\n</div>\n<div class=\"webpage-preview\">\n<div class=\"webpage-preview\" style=\"border-left: 3px solid #ccc; padding-left: 10px; margin: 10px 0;\">\n<div class=\"webpage-title\" style=\"font-weight:bold; margin:5px 0;\">\n<a href=\"https://example.com\" target=\"_blank\">Example</a></div>\n<div class=\"webpage-url\" style=\"color:#666; font-size:0.9em;margin-bottom:5px;\">https://example.com</div>\n</div>\n</div>"
|
||||
"html": "<div class=\"message-media\">\n<div class=\"webpage-preview\">\n<div class=\"webpage-preview\" style=\"border-left: 3px solid #ccc; padding-left: 10px; margin: 10px 0;\">\n<div class=\"webpage-title\" style=\"font-weight:bold; margin:5px 0;\">\n<a href=\"https://example.com\" target=\"_blank\">Example</a></div>\n<div class=\"webpage-url\" style=\"color:#666; font-size:0.9em;margin-bottom:5px;\">https://example.com</div>\n</div>\n</div>"
|
||||
}
|
||||
}
|
||||
@@ -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():
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user