[bug+test] Остаток после QA/тестов: 2 бага потери данных (#206) + оставшиеся фазы тест-стратегии (#204) #244

Open
opened 2026-06-28 03:42:57 +03:00 by Ghost · 1 comment

Единый трекер невыполненного остатка после закрытия #206 и #204. Обе исходные задачи закрыты как «по сути готовые» (тесты написаны, приоритетный поднабор реализован в PR #230, большинство находок редтима — в PR #212). Здесь собрано всё, что осознанно осталось, чтобы долг не потерялся.

Часть A — баги потери данных (остаток #206)

Два реально подтверждённых бага, в PR #230 осознанно оставлены неисправленными (их фикс — изменение поведения) и лишь задокументированы характеризующими тестами (it.fails / it.failing).

mdrt-2 — Экспорт в Markdown молча выкидывает узлы без turndown-правила (high)

Страница с transclusionReference / mention / status / pageBreak при Export to Markdown / copy-as-markdown: transclusionReference и pageBreak исчезают полностью; mention/status теряют идентичность (data-id/color). Нарушен инвариант «никогда молча не терять блок».

  • Док-тест (в дереве): packages/editor-ext/src/lib/markdown/utils/turndown.dataloss.test.ts (характеризация + it.fails).
  • Источники: turndown.utils.ts, transclusion-reference.ts, mention.ts.
  • Контракт-цель: каждый кастом-узел при экспорте сериализуется в lossless-форму (raw-HTML-плейсхолдер) либо в осознанный документированный плейсхолдер, но НЕ исчезает.

persist-6 — Пустой live-документ перезаписывает непустой контент (high)

В onStoreDocument нет empty-guard перед updatePage (в отличие от onLoadDocument). Если live Y.Doc на мгновение сериализуется в пустой/почти-пустой — пустой контент проходит и затирает страницу.

  • Док-тест (в дереве): apps/server/src/collaboration/extensions/persistence-store.spec.ts (it.failing).
  • Источник: persistence.extension.ts (isEmptyParagraphDoc используется только для boundary-снимка).
  • Контракт-цель: store-side empty-guard — не записывать пустой doc поверх непустого без явного сигнала.

DoD части A

  • mdrt-2: ни один кастом-узел не теряется при экспорте MD; turndown.dataloss.test.ts it.fails → зелёный.
  • persist-6: пустой live-doc не затирает непустую страницу; persistence-store.spec.ts it.failing → зелёный.

Часть B — оставшиеся фазы тест-стратегии (остаток #204)

Уже сделано (PR #230)

  • Фаза 1: mcp-clients.lease.spec.ts (lease/refcount/eviction MCP-клиента).
  • Фаза 2 (частично): editor-ext table-utils + math-tokenizer false-positive; client emoji-menu (+it.fails на JSON.parse), sort-cells, normalizeTableColumnWidths; mcp htmlEmbed/pageBreak data-loss + footnote-diff; server export getInternalLinkPageName (исправлен + тест).

Остаток

Фаза 2 (хвост)

  • ai-chat: SSRF guardedFetch/lookup, decideClose; decryptHeaders fail-open.
  • editor-ext: recreateTransform-инвариант (apply(diff)=target), moveRow/moveColumn, getSelectionRangeInColumn, addUniqueIdsToDoc.
  • client: ordered-emitter диктовки, paste-хелперы, decideTurnEnd/sendNow.
  • mcp: round-trip медиа (video/youtube/embed/excalidraw/audio/pdf), cookie-парсинг performLogin, applyAnchorInDoc.

Фаза 3 — серверная безопасность/доступ (нужен DB-харнесс Postgres + R-core)

  • getShareForPage наследование, isSharingAllowed toggle, getShareTree restricted-ancestor, lookupTransclusionForShare access-граф, acceptInvitation атомарность.
  • AiSettingsService (секреты write-only, неутечка ключей), environment.validation (после снятия process.exit).
  • common: yjs.util (setYjsMark/removeYjsMarkByAttribute/updateYjsMarkAttribute) — 0 тестов.

Фаза 4 — добивка и контракты

  • client tree-reducers/utils (applyUpdateOne, tree-utils, formatRelativeTime), transclusion-lookup-context.
  • contract: паритет SHARED_TOOL_SPECS ↔ in-app/MCP; дрейф recreate-transform (vendored editor-ext vs npm в mcp/diff.ts).
  • замена тавтологичных спеков (storage.service.spec.ts, environment.service.spec.ts) на проверки делегирования.
  • фикс findBreadcrumbPath-мутации входа.

Инфраструктура

  • @vitest/coverage-v8 и/или починка istanbul на ESM-импорте @docmost/editor-ext, порог покрытия (сейчас coverage-gate отсутствует).
  • DB-харнесс (Postgres) — блокирует 5 share/invitation integration-тестов.

Полный детальный план по модулям — в истории #204.

> Единый трекер невыполненного остатка после закрытия #206 и #204. Обе исходные задачи закрыты как «по сути готовые» (тесты написаны, приоритетный поднабор реализован в PR #230, большинство находок редтима — в PR #212). Здесь собрано всё, что осознанно осталось, чтобы долг не потерялся. # Часть A — баги потери данных (остаток #206) Два реально подтверждённых бага, в PR #230 осознанно **оставлены неисправленными** (их фикс — изменение поведения) и лишь задокументированы характеризующими тестами (`it.fails` / `it.failing`). ## mdrt-2 — Экспорт в Markdown молча выкидывает узлы без turndown-правила (high) Страница с `transclusionReference` / `mention` / `status` / `pageBreak` при Export to Markdown / copy-as-markdown: `transclusionReference` и `pageBreak` исчезают полностью; `mention`/`status` теряют идентичность (`data-id`/`color`). Нарушен инвариант «никогда молча не терять блок». - Док-тест (в дереве): `packages/editor-ext/src/lib/markdown/utils/turndown.dataloss.test.ts` (характеризация + `it.fails`). - Источники: `turndown.utils.ts`, `transclusion-reference.ts`, `mention.ts`. - Контракт-цель: каждый кастом-узел при экспорте сериализуется в lossless-форму (raw-HTML-плейсхолдер) либо в осознанный документированный плейсхолдер, но НЕ исчезает. ## persist-6 — Пустой live-документ перезаписывает непустой контент (high) В `onStoreDocument` нет empty-guard перед `updatePage` (в отличие от `onLoadDocument`). Если live `Y.Doc` на мгновение сериализуется в пустой/почти-пустой — пустой контент проходит и затирает страницу. - Док-тест (в дереве): `apps/server/src/collaboration/extensions/persistence-store.spec.ts` (`it.failing`). - Источник: `persistence.extension.ts` (`isEmptyParagraphDoc` используется только для boundary-снимка). - Контракт-цель: store-side empty-guard — не записывать пустой doc поверх непустого без явного сигнала. ### DoD части A - [ ] mdrt-2: ни один кастом-узел не теряется при экспорте MD; `turndown.dataloss.test.ts` `it.fails` → зелёный. - [ ] persist-6: пустой live-doc не затирает непустую страницу; `persistence-store.spec.ts` `it.failing` → зелёный. --- # Часть B — оставшиеся фазы тест-стратегии (остаток #204) ## Уже сделано (PR #230) - **Фаза 1:** `mcp-clients.lease.spec.ts` (lease/refcount/eviction MCP-клиента). - **Фаза 2 (частично):** editor-ext table-utils + math-tokenizer false-positive; client emoji-menu (+`it.fails` на `JSON.parse`), sort-cells, `normalizeTableColumnWidths`; mcp htmlEmbed/pageBreak data-loss + footnote-diff; server export `getInternalLinkPageName` (исправлен + тест). ## Остаток ### Фаза 2 (хвост) - ai-chat: SSRF `guardedFetch`/`lookup`, `decideClose`; `decryptHeaders` fail-open. - editor-ext: `recreateTransform`-инвариант (apply(diff)=target), `moveRow`/`moveColumn`, `getSelectionRangeInColumn`, `addUniqueIdsToDoc`. - client: ordered-emitter диктовки, paste-хелперы, `decideTurnEnd`/`sendNow`. - mcp: round-trip медиа (`video/youtube/embed/excalidraw/audio/pdf`), cookie-парсинг `performLogin`, `applyAnchorInDoc`. ### Фаза 3 — серверная безопасность/доступ (нужен DB-харнесс Postgres + R-core) - `getShareForPage` наследование, `isSharingAllowed` toggle, `getShareTree` restricted-ancestor, `lookupTransclusionForShare` access-граф, `acceptInvitation` атомарность. - `AiSettingsService` (секреты write-only, неутечка ключей), `environment.validation` (после снятия `process.exit`). - common: `yjs.util` (`setYjsMark`/`removeYjsMarkByAttribute`/`updateYjsMarkAttribute`) — 0 тестов. ### Фаза 4 — добивка и контракты - client tree-reducers/utils (`applyUpdateOne`, tree-utils, `formatRelativeTime`), `transclusion-lookup-context`. - contract: паритет `SHARED_TOOL_SPECS` ↔ in-app/MCP; дрейф `recreate-transform` (vendored editor-ext vs npm в `mcp/diff.ts`). - замена тавтологичных спеков (`storage.service.spec.ts`, `environment.service.spec.ts`) на проверки делегирования. - фикс `findBreadcrumbPath`-мутации входа. ### Инфраструктура - `@vitest/coverage-v8` и/или починка istanbul на ESM-импорте `@docmost/editor-ext`, порог покрытия (сейчас coverage-gate отсутствует). - DB-харнесс (Postgres) — блокирует 5 share/invitation integration-тестов. Полный детальный план по модулям — в истории #204.
Ghost added the bug label 2026-06-28 03:42:57 +03:00
Ghost changed title from [bug][data-loss] Экспорт Markdown теряет ноды (mdrt-2) + пустой live-документ затирает контент (persist-6) to [bug+test] Остаток после QA/тестов: 2 бага потери данных (#206) + оставшиеся фазы тест-стратегии (#204) 2026-06-28 03:45:57 +03:00
Ghost added the test label 2026-06-28 03:46:13 +03:00
Collaborator

Часть B — крупный no-DB чанк сделан в PR #257 (f9b58a0e): +193 теста (editor-ext recreateTransform/move-row/col/getSelectionRange/uniqueId; mcp auth-cookie/media-attrs/applyAnchor/recreate-drift; client findBreadcrumbPath-non-mutation/applyUpdateOne/formatRelativeTime/sortPositionKeys; server SSRF guardedFetch+decryptHeaders/yjs.util[было 0]/SHARED_TOOL_SPECS-контракт[76]/storage-делегирование) + fix findBreadcrumbPath-мутации + behavior-identical вынос extractAuthTokenFromSetCookie.

Остаётся в #244 (deferred, follow-up):

  • Фаза 3 (share/invitation/AiSettings) — нужен Postgres DB-харнесс (*.int-spec против gitmost-test-pg).
  • Embedded client logic: sendNow, dictation ordered-emitter — не pure-экспорты, нужен аккуратный рефактор компонентов.
  • Coverage-gate инфра (@vitest/coverage-v8 / istanbul-on-ESM / threshold).

Часть A (mdrt-2/persist-6) закрыта в #248.

Часть B — крупный no-DB чанк сделан в PR #257 (f9b58a0e): +193 теста (editor-ext recreateTransform/move-row/col/getSelectionRange/uniqueId; mcp auth-cookie/media-attrs/applyAnchor/recreate-drift; client findBreadcrumbPath-non-mutation/applyUpdateOne/formatRelativeTime/sortPositionKeys; server SSRF guardedFetch+decryptHeaders/yjs.util[было 0]/SHARED_TOOL_SPECS-контракт[76]/storage-делегирование) + fix findBreadcrumbPath-мутации + behavior-identical вынос extractAuthTokenFromSetCookie. **Остаётся в #244 (deferred, follow-up):** - Фаза 3 (share/invitation/AiSettings) — нужен Postgres DB-харнесс (*.int-spec против gitmost-test-pg). - Embedded client logic: sendNow, dictation ordered-emitter — не pure-экспорты, нужен аккуратный рефактор компонентов. - Coverage-gate инфра (@vitest/coverage-v8 / istanbul-on-ESM / threshold). Часть A (mdrt-2/persist-6) закрыта в #248.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#244