Skip to content

test(editor): golden round-trip conformance suite across both serializers - #1874

Merged
h4yfans merged 9 commits into
mainfrom
roundtrip-conformance-suite
Aug 28, 2026
Merged

test(editor): golden round-trip conformance suite across both serializers#1874
h4yfans merged 9 commits into
mainfrom
roundtrip-conformance-suite

Conversation

@h4yfans

@h4yfans h4yfans commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

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:

  • Renderer halfroundtrip-conformance.test.ts (renderer suite): a REAL BlockNoteEditor on the real editor schema, through parseMarkdownPreservingBlanksnormalizeNoteBlocksserializeBlocksPreservingBlanks — the actual open→save cycle, real remark escaping included. First unmocked renderer round-trip coverage.
  • Main halfblocknote-converter.roundtrip.test.ts (main suite): markdownToYFragmentyDocToMarkdown, extending the assertions already in blocknote-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

Family Cases
Link mention URLs _ * ! ~ ' ( ) % & = #, spaces, non-ASCII, trailing ), kitchen-sink, underscore-next-to-punctuation
Date pills body text, crafted base64url __/-- emphasis runs, reminder variants, two-in-a-line
Callouts all four types, multi-line body, foreign > [!note]/> [!tip] passthrough, title-after-marker, multi-paragraph body, nested foreign
Toggles empty, body, nested, blank lines, code fence, image, unterminated
Containers mention+date in table cells / list items / toggle bodies, wiki link + hash tag in cells and sentences, callout in toggle
Block markers youtube embed, bookmark, file marker, task line
Golden vault four new roundtrip-*.md fixtures, byte-gated by the existing frontmatter suite AND pushed through the converter

Fuzz

No property-testing dep in the workspace and pnpm add is 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.fails tagged 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:

#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 of normalizeNoteBlocks (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 existing app_error_seen channel as action: editor_unclaimed_token, errorCode: unclaimed_{mention,date,callout_marker}, count in metrics.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 in architecture/observability.md.

CI note

check:architecture (inside pnpm 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

  • main suite: 48 passed, 5 expected-fail · renderer suite: 38 passed, 10 expected-fail · telemetry: 7 passed
  • pnpm lint 0 errors; typecheck:node/web/test green (new test files compile, exclude backlog untouched); docs:impact --strict covered; docs:build green; full main project: only the pre-existing check-cert-hashes-config local-env failures (identical on untouched main); full renderer project: failures identical to the pre-existing set on main (baseline diff run to confirm).

@github-actions github-actions Bot added dependencies documentation Improvements or additions to documentation test labels Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 7e28137.

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.73418% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nts/note/content-area/unclaimed-token-telemetry.ts 98.68% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@h4yfans
h4yfans force-pushed the roundtrip-conformance-suite branch 3 times, most recently from 9a87796 to 196a1f3 Compare August 28, 2026 00:15
…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.
Expanded toggles (the new open-attribute on-disk form) join the corpus,
the golden fixture and the fuzz roll. The unterminated shape #1876 did
not reach re-anchors to #1883: the declined region's raw-HTML lines are
still dropped by the markdown parser on both pipelines.
Every mention payload and the mention fuzz family now assert fixed
behavior on both pipelines. The renderer's rich render-in-a-cell rewrite
was not in #1867's scope and joins its wiki-link twin on #1865.
@h4yfans
h4yfans force-pushed the roundtrip-conformance-suite branch from 196a1f3 to 7e28137 Compare August 28, 2026 01:02
@h4yfans
h4yfans merged commit b63ac44 into main Aug 28, 2026
17 checks passed
@h4yfans
h4yfans deleted the roundtrip-conformance-suite branch August 28, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies documentation Improvements or additions to documentation test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Golden round-trip conformance suite across the renderer and main serializers

1 participant