test(editor): golden round-trip conformance suite across both serializers - #1874
Merged
Conversation
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
h4yfans
force-pushed
the
roundtrip-conformance-suite
branch
3 times, most recently
from
August 28, 2026 00:15
9a87796 to
196a1f3
Compare
…zers One corpus in @memry/editor-schema/conformance, asserted byte-for-byte by both pipelines: the renderer save path (real BlockNoteEditor + the real editor schema, markdown-utils + normalize-note-blocks) and the main/CRDT converter (markdownToYFragment -> yDocToMarkdown). Each case asserts round-trip identity and second-pass idempotence against the same canonical bytes, so cross-serializer agreement is a tested equality. Covers the payload table of #1848: mention URLs with markdown-significant characters, date tokens with base64url emphasis runs, all four callout types plus foreign passthrough / titles / multi-paragraph bodies, toggles empty / nested / blank-lined / fenced / imaged / unterminated, and tokens inside table cells, list items and toggle bodies — plus a deterministic seeded fuzz layer over the token alphabets (no property-testing dep in the workspace). Cases broken on current main are marked it.fails pending their sibling fix (#1844 #1846 #1847, plus #1865 found by this suite), so the sibling landing turns the inverted expectation red and forces the flag off. Golden vault gains four roundtrip-*.md fixtures, byte-gated by the existing frontmatter suite and now also pushed through the converter. New renderer-side counter reports tokens the normalize chain left unclaimed through app_error_seen (leading emit, then one aggregated event per kind per minute). Metric only, no user-facing surface.
it.each on an empty list throws, so the it.fails buckets are registered only while pending cases exist; conformance internals stop being exported.
New mixed-documents family assembles notes from every token family plus lists, paragraphs and gaps. It immediately caught #1877 — blank-line gaps adjacent to a toggle collapse on write-back — now pinned as a static pending case; the family itself joins with single gaps and never puts two lists across one blank line (CommonMark fuses those: accepted canonicalization, not token damage).
The hostile anchor bytes that forced base64url emphasis runs now assert the opposite property: the fixed encoder emits no emphasis character at all, so a future alphabet reopen fails the corpus loudly.
Foreign passthrough, titles after the marker and the four claimed types now round-trip on both pipelines as plain assertions. The two blank-`>`- line shapes #1875 explicitly declined re-anchor to #1881: the quote path collapses blank quote lines on ANY blockquote, callout or not, merging quoted paragraphs on write-back.
h4yfans
force-pushed
the
roundtrip-conformance-suite
branch
from
August 28, 2026 01:02
196a1f3 to
7e28137
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1848. Part of epic #1843 — this is the suite that makes the whole token class fail in CI instead of in a user's inbox.
Shape
One corpus,
@memry/editor-schema/conformance, asserted byte-for-byte by both pipelines:roundtrip-conformance.test.ts(renderer suite): a REALBlockNoteEditoron the real editor schema, throughparseMarkdownPreservingBlanks→normalizeNoteBlocks→serializeBlocksPreservingBlanks— the actual open→save cycle, real remark escaping included. First unmocked renderer round-trip coverage.blocknote-converter.roundtrip.test.ts(main suite):markdownToYFragment→yDocToMarkdown, extending the assertions already inblocknote-converter.test.ts.Each case asserts (1) round-trip identity against canonical bytes, (2) second-pass idempotence (write-back never rewrites what it just wrote). Both halves target the SAME canonical bytes, so cross-serializer byte equality is a tested equality, not an assumption.
Coverage
_ * ! ~ ' ( ) % & = #, spaces, non-ASCII, trailing), kitchen-sink, underscore-next-to-punctuation__/--emphasis runs, reminder variants, two-in-a-line> [!note]/> [!tip]passthrough, title-after-marker, multi-paragraph body, nested foreignroundtrip-*.mdfixtures, byte-gated by the existing frontmatter suite AND pushed through the converterFuzz
No property-testing dep in the workspace and
pnpm addis off-limits, so a seeded mulberry32 drives 48 deterministic cases per family (mention URL alphabet, date payload alphabet incl. bytes that force base64url_/-, callout bodies, structured toggle bodies incl. nesting and fences). Same bytes every CI run. Free-text alphabets exclude emphasis pairs/backticks deliberately: remark canonicalizes those in ANY paragraph — accepted app-wide behavior, not this bug class. Inside a token nothing may be rewritten, so the token alphabets keep every hostile character.Expected failures (the two-phase contract)
Cases broken on current main run as
it.failstagged with the sibling that fixes them — when that sibling lands, the inverted expectation turns red and forces the flag off, converting the case into a permanent assertion of the fixed behavior:renderrewrites the token as a markdown link), mention URL fuzz family (both).[!note]/[!tip]rewritten to[!info](renderer), title-after-marker split (renderer), multi-paragraph body collapse (both), nested foreign callouts (both).<details data-memry-toggle>currently DROPS its open/summary lines on both pipelines.[[wiki link]]in a table cell serializes as bare display text on the renderer path.#1845 (date pills): every enumerated and fuzzed payload already round-trips on both pipelines — the suite locks that in as plain green assertions.
Telemetry
unclaimed-token-telemetry.ts, wired at the end ofnormalizeNoteBlocks(every note surface runs that chain): counts((mention:/((date:still literal after promotion and paragraph-leading orphaned[!type]markers, skipping code blocks. Reports through the existingapp_error_seenchannel asaction: editor_unclaimed_token,errorCode: unclaimed_{mention,date,callout_marker}, count inmetrics.itemCount. Leading emit on first sighting, then at most one aggregated event per kind per minute — the chain re-runs per note open/remote update and the main-side error path is itself per-minute throttled, so the counter aggregates instead of spamming. Metric only, no toast. Documented inarchitecture/observability.md.CI note
check:architecture(insidepnpm typecheck) is red on current main for every branch (apps/mobile/vitest.config.ts -> node:url); fix is #1868. This branch stays pure of that fix and goes green once #1868 merges and it rebases.Verification
pnpm lint0 errors;typecheck:node/web/testgreen (new test files compile, exclude backlog untouched);docs:impact --strictcovered;docs:buildgreen; full main project: only the pre-existingcheck-cert-hashes-configlocal-env failures (identical on untouched main); full renderer project: failures identical to the pre-existing set on main (baseline diff run to confirm).