Files
gitmost/packages/mcp/test/unit/footnote-corpus.mjs
a c4ed4a4855 fix(footnotes): strip bare definitions on rebuild; MCP full-doc + zip-import canonicalize tests (#228)
Review #6 (approve-with-comments) follow-ups:
1. canonicalize step 7 now strips bare footnoteDefinitions at ANY depth
   (stripFootnoteDefinitionsDeep), not just footnotesList, in BOTH copies. A
   definition hand-authored outside a list (e.g. nested in a callout via a
   raw-JSON write path) was left in place while a copy was also added to the
   rebuilt list -> duplicate, idempotent, self-perpetuating. Runs only in the
   rebuild path (after the lists are stripped); the fast-path / placement-keep
   branch is untouched. Added a shared-corpus case (bare def nested in a callout)
   to pin it in both mirrors.
2. markdown-clipboard: removed the dead top-level footnoteReference check in
   canonicalizePastedFootnotes (an inline atom is never a top-level slice child;
   only the descendants scan can find it).

Test coverage:
4. New MCP binding tests (full-doc-write-canonicalize.test.mjs): update_page_json
   and copy_page_content canonicalize the persisted full doc, asserted via a new
   `replacePage` seam (symmetric to the existing `mutatePage` seam) so no live
   collab socket is needed. Routed both writers through the seam.
5. New server spec (file-import-task.service.footnote-canonicalize.spec.ts): the
   zip-import path (processGenericImport) canonicalizes footnotes — real
   markdown->HTML->JSON via a real ImportService over a temp-dir .md file, DB trx
   stubbed to capture the persisted page content. FileImportTaskService had no
   spec before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 01:39:25 +03:00

1256 lines
27 KiB
JavaScript

// MIRROR (data only) of
// packages/editor-ext/src/lib/footnote/footnote-corpus.ts — keep the two in
// sync. Shared golden corpus for the footnote canonicalizer (issue #228): each
// case is { name, input, expected } where `expected` is exactly what
// `canonicalizeFootnotes(input)` must return. Running BOTH the editor-ext copy
// and this MCP mirror against the same corpus makes "the two pure copies behave
// identically" a checkable property without coupling the packages.
export const FOOTNOTE_CORPUS = [
{
"name": "out-of-order defs ordered by first reference",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "b"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "c"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "c"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "C"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "b"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "B"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "D"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "b"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "c"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "b"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "B"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "D"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "c"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "C"
}
]
}
]
}
]
}
]
}
},
{
"name": "orphan definition dropped",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "orphan"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "O"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
}
]
}
]
}
},
{
"name": "no references removes the list",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "plain"
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "orphan"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "O"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "plain"
}
]
}
]
}
},
{
"name": "reuse: repeated references collapse to one definition",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "text",
"text": " a "
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "shared"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "text",
"text": " a "
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "shared"
}
]
}
]
}
]
}
]
}
},
{
"name": "duplicate definitions: first wins",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "first"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "second"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "third"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "d"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "d"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "first"
}
]
}
]
}
]
}
]
}
},
{
"name": "synthesizes an empty definition for a reference with none",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "missing"
}
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "missing"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "missing"
},
"content": [
{
"type": "paragraph"
}
]
}
]
}
]
}
},
{
"name": "merges multiple footnotesList nodes into one",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "a"
},
{
"type": "footnoteReference",
"attrs": {
"id": "x"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "y"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "x"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "X"
}
]
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "tail"
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "y"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Y"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "a"
},
{
"type": "footnoteReference",
"attrs": {
"id": "x"
}
},
{
"type": "footnoteReference",
"attrs": {
"id": "y"
}
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "tail"
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "x"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "X"
}
]
}
]
},
{
"type": "footnoteDefinition",
"attrs": {
"id": "y"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Y"
}
]
}
]
}
]
}
]
}
},
{
"name": "single canonical list before a trailing empty paragraph stays put",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
}
]
},
{
"type": "paragraph"
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
}
]
},
{
"type": "paragraph"
}
]
}
},
{
"name": "single canonical list with NON-EMPTY content after it is NOT moved (plugin parity)",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "epilogue text"
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "x"
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A"
}
]
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "epilogue text"
}
]
}
]
}
},
{
"name": "reference inside a nested container (callout) is collected",
"input": {
"type": "doc",
"content": [
{
"type": "callout",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "see "
},
{
"type": "footnoteReference",
"attrs": {
"id": "n"
}
}
]
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "n"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "note"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "callout",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "see "
},
{
"type": "footnoteReference",
"attrs": {
"id": "n"
}
}
]
}
]
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "n"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "note"
}
]
}
]
}
]
}
]
}
},
{
"name": "bare footnoteDefinition nested in a callout is collected, NOT duplicated",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "see "
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "callout",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "note A"
}
]
}
]
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "see "
},
{
"type": "footnoteReference",
"attrs": {
"id": "a"
}
}
]
},
{
"type": "callout",
"content": []
},
{
"type": "footnotesList",
"content": [
{
"type": "footnoteDefinition",
"attrs": {
"id": "a"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "note A"
}
]
}
]
}
]
}
]
}
},
{
"name": "no footnotes at all is unchanged",
"input": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "just text"
}
]
}
]
},
"expected": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "just text"
}
]
}
]
}
}
];