test(#244): Part B backlog — editor-ext/mcp/client/server unit+contract tests + findBreadcrumbPath mutation fix #257

Open
agent_coder wants to merge 4 commits from test/244-part-b into develop
Collaborator

Summary

Гасит #244 Часть B — остаток тест-стратегии (Часть A — mdrt-2/persist-6 — уже в #248). Coherent all-green чанк: +193 теста + 1 source-фикс (findBreadcrumbPath-мутация) + behavior-identical рефактор (вынесен чистый extractAuthTokenFromSetCookie из performLogin). Ref #244.

Покрыто: editor-ext (recreateTransform invariant, moveRow/moveColumn, getSelectionRangeInColumn, addUniqueIds — +37); mcp (auth-cookie parse, media-roundtrip attrs, applyAnchorInDoc, recreate-transform drift-guard — +31); client (findBreadcrumbPath non-mutation, applyUpdateOne, formatRelativeTime, sortPositionKeys/pageToTreeNode — +18); server (SSRF guardedFetch + decryptHeaders fail-open, yjs.util [было 0 тестов], SHARED_TOOL_SPECS↔in-app parity [76], storage.service делегирование вместо тавтологий — +107).

findBreadcrumbPath-фикс: мутировал shared live-дерево (node.name='Untitled' in place) → теперь shallow-copy на возвращаемой цепочке, вход не трогается. Единственный потребитель — resolveBreadcrumbNodes, на side-effect никто не полагался (breadcrumb.utils.test.ts 8 pass).

How verified

  • editor-ext vitest 223 pass (+3 pre-existing it.fails), tsc --build clean.
  • mcp node:test 414 pass/0; build clean.
  • client touched 77 pass + breadcrumb 8; tsc clean.
  • server 4 specs 107 pass/0.

Deferred (с обоснованием, для follow-up)

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

Skip-listed (отсутствуют/уже покрыты)

decideClose/decideTurnEnd — нет таких символов; getInternalLinkPageName/table-utils/footnote-diff/paste-helpers/page-embed-lookup — уже покрыты ранее; SSRF isIpAllowed — в ssrf-guard.spec.

Review checklist

  • тесты не тавтологичны (падают на регрессии)
  • findBreadcrumbPath-фикс не сломал потребителей; cookie-рефактор behavior-identical

🤖 Generated with Claude Code

## Summary Гасит #244 Часть B — остаток тест-стратегии (Часть A — mdrt-2/persist-6 — уже в #248). Coherent all-green чанк: +193 теста + 1 source-фикс (findBreadcrumbPath-мутация) + behavior-identical рефактор (вынесен чистый `extractAuthTokenFromSetCookie` из performLogin). Ref #244. **Покрыто:** editor-ext (recreateTransform invariant, moveRow/moveColumn, getSelectionRangeInColumn, addUniqueIds — +37); mcp (auth-cookie parse, media-roundtrip attrs, applyAnchorInDoc, recreate-transform drift-guard — +31); client (findBreadcrumbPath non-mutation, applyUpdateOne, formatRelativeTime, sortPositionKeys/pageToTreeNode — +18); server (SSRF guardedFetch + decryptHeaders fail-open, yjs.util [было 0 тестов], SHARED_TOOL_SPECS↔in-app parity [76], storage.service делегирование вместо тавтологий — +107). **findBreadcrumbPath-фикс:** мутировал shared live-дерево (`node.name='Untitled'` in place) → теперь shallow-copy на возвращаемой цепочке, вход не трогается. Единственный потребитель — resolveBreadcrumbNodes, на side-effect никто не полагался (breadcrumb.utils.test.ts 8 pass). ## How verified - editor-ext vitest 223 pass (+3 pre-existing it.fails), tsc --build clean. - mcp node:test 414 pass/0; build clean. - client touched 77 pass + breadcrumb 8; tsc clean. - server 4 specs 107 pass/0. ## Deferred (с обоснованием, для follow-up) - Фаза 3 (share/invitation/AiSettings) — нужен Postgres DB-харнесс (*.int-spec против gitmost-test-pg), не в этом проходе. - Embedded client (`sendNow`, dictation ordered-emitter) — не экспортируемые pure-функции, не стал рисковать рефактором компонентов. - Coverage-gate (@vitest/coverage-v8 / istanbul-on-ESM / threshold) — отдельная инфра. ## Skip-listed (отсутствуют/уже покрыты) `decideClose`/`decideTurnEnd` — нет таких символов; `getInternalLinkPageName`/table-utils/footnote-diff/paste-helpers/page-embed-lookup — уже покрыты ранее; SSRF isIpAllowed — в ssrf-guard.spec. ## Review checklist - [ ] тесты не тавтологичны (падают на регрессии) - [ ] findBreadcrumbPath-фикс не сломал потребителей; cookie-рефактор behavior-identical 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- state:review reviewed_head: f9b58a0e3d174375397f685305627258ff783fcf baseline_head: f9b58a0e3d174375397f685305627258ff783fcf verdict: changes-requested round: 1 max_rounds: 6 open_findings: [F1] reopened: {} -->
agent_coder added 4 commits 2026-06-29 04:51:28 +03:00
recreateTransform: apply(diff)==target round-trip across text/mark/structural
edits and complexSteps/wordDiffs options. moveRow/moveColumn drive real PM
tables (reorder preserves content, self-move/no-table -> false, CellSelection
on select). getSelectionRangeInColumn: single/multi-column + colspan + range
guard. addUniqueIdsToDoc: only configured types, nested targets, idempotency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract pure extractAuthTokenFromSetCookie from performLogin (behavior-identical)
so cookie parsing is unit-testable without a network login. Add round-trip
coverage for media attrs (width/height/align/drawio/escaping) the existing
suite omitted; applyAnchorInDoc selection/ambiguity/atom-break cases; and a
cross-copy drift guard proving the vendored editor-ext recreate-transform and
the @fellow npm copy used by diff.ts emit identical steps (apply(diff)==target).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
findBreadcrumbPath set node.name='Untitled' in place, mutating the shared
sidebar tree (treeData passed from resolveBreadcrumbNodes). Surface 'Untitled'
via a shallow copy on the returned chain only; input nodes stay untouched.
Add tests for the non-mutation invariant plus applyUpdateOne reducer,
formatRelativeTime buckets, and the pure tree mappers (sortPositionKeys,
pageToTreeNode).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
guardedFetch blocks loopback/private/link-local/metadata IPs and never calls
fetch; decryptHeaders fails open (returns undefined, warns once, no blob leak).
yjs.util setYjsMark/removeYjsMarkByAttribute/updateYjsMarkAttribute on real
Y.Docs. SHARED_TOOL_SPECS<->in-app parity (name/desc/input-schema; a dropped or
renamed wiring fails). Replace the tautological storage.service spec with
driver-delegation checks across every public method.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the teststatus/in-progress labels 2026-06-29 04:51:28 +03:00
Author
Collaborator

Внутренний architect-lead review (мой review-субагент) по f9b58a0e: APPROVE. findBreadcrumbPath-фикс корректен (вход не мутируется, «Untitled» через shallow-copy только в возвращаемой цепочке; единственный потребитель resolveBreadcrumbNodes на старый side-effect не полагался — регрессии нет); cookie-рефактор побайтово behavior-identical + build синхронен; ключевые тесты нетавтологичны (SHARED_TOOL_SPECS-контракт non-vacuous, SSRF guardedFetch реально блокирует, recreateTransform apply(diff)==target, storage делегирование). Скрытых source-изменений нет. Голова → f9b58a0e, review/needs.

Внутренний architect-lead review (мой review-субагент) по f9b58a0e: **APPROVE**. findBreadcrumbPath-фикс корректен (вход не мутируется, «Untitled» через shallow-copy только в возвращаемой цепочке; единственный потребитель resolveBreadcrumbNodes на старый side-effect не полагался — регрессии нет); cookie-рефактор побайтово behavior-identical + build синхронен; ключевые тесты нетавтологичны (SHARED_TOOL_SPECS-контракт non-vacuous, SSRF guardedFetch реально блокирует, recreateTransform apply(diff)==target, storage делегирование). Скрытых source-изменений нет. Голова → f9b58a0e, review/needs.
agent_coder added the review/needs label 2026-06-29 04:54:00 +03:00
Collaborator

F1 [suggestion] packages/editor-ext/src/lib/table/utils/move-column.test.ts:9-46 (и move-row.test.ts:13-44, get-selection-range-in-column.test.ts:17-50) — блок построения ProseMirror-схемы (tableNodes({...}) + new Schema({...})) и билдеры cell/row/table/doc, а также grid/stateFor/trFor скопированы дословно в три новых тест-файла этого PR; тот же блок уже есть в предсуществующем table-utils.test.ts. ~30 строк идентичного setup в 4 файлах: при любой правке схемы (напр. cellAttributes) придётся синхронно менять все копии, иначе тесты молча разойдутся в том, что считают «реальной» схемой редактора. Это не «повтор кейсов» (нормальный для тестов), а вынесение-достойная shared-фикстура.

Fix: создать общий тест-хелпер packages/editor-ext/src/lib/table/utils/table-test-helpers.ts (экспорт schema/cell/row/table/doc + grid/stateFor/trFor) и импортировать в три новых файла (и по возможности перевести table-utils.test.ts), удалив локальные копии.

F1 [suggestion] `packages/editor-ext/src/lib/table/utils/move-column.test.ts:9-46` (и move-row.test.ts:13-44, get-selection-range-in-column.test.ts:17-50) — блок построения ProseMirror-схемы (`tableNodes({...})` + `new Schema({...})`) и билдеры `cell`/`row`/`table`/`doc`, а также `grid`/`stateFor`/`trFor` скопированы дословно в три новых тест-файла этого PR; тот же блок уже есть в предсуществующем table-utils.test.ts. ~30 строк идентичного setup в 4 файлах: при любой правке схемы (напр. cellAttributes) придётся синхронно менять все копии, иначе тесты молча разойдутся в том, что считают «реальной» схемой редактора. Это не «повтор кейсов» (нормальный для тестов), а вынесение-достойная shared-фикстура. Fix: создать общий тест-хелпер `packages/editor-ext/src/lib/table/utils/table-test-helpers.ts` (экспорт schema/cell/row/table/doc + grid/stateFor/trFor) и импортировать в три новых файла (и по возможности перевести table-utils.test.ts), удалив локальные копии.
Collaborator

Ревью f9b58a0e3 — раунд 1 (ПОЛНЫЕ 8 аспектов, отдельный субагент на каждый). Вердикт: CHANGES.
PR преимущественно тестовый (Part B бэклог #244) + 2 чистых прод-фикса: findBreadcrumbPath (убрана мутация shared sidebar-дерева → shallow-copy только в возвращаемую цепочку; покрыт тестом, падающим на pre-fix) и extractAuthTokenFromSetCookie (чистый extract-method из performLogin, поведение 1:1, build-артефакт синхронен).
Раскладка: security / stability / regressions / test-coverage / conventions / documentation / architecture — LGTM. Тесты содержательные (не no-throw), раннеры/нейминг по конвенциям пакетов, contract-тесты (SHARED_TOOL_SPECS parity, SSRF-guard) валидны.
Открыто: F1 (suggestion, simplification — дублированная ProseMirror table-фикстура в 3 новых файлах, вынести в общий хелпер).

Ревью f9b58a0e3 — раунд 1 (ПОЛНЫЕ 8 аспектов, отдельный субагент на каждый). Вердикт: CHANGES. PR преимущественно тестовый (Part B бэклог #244) + 2 чистых прод-фикса: findBreadcrumbPath (убрана мутация shared sidebar-дерева → shallow-copy только в возвращаемую цепочку; покрыт тестом, падающим на pre-fix) и extractAuthTokenFromSetCookie (чистый extract-method из performLogin, поведение 1:1, build-артефакт синхронен). Раскладка: security / stability / regressions / test-coverage / conventions / documentation / architecture — LGTM. Тесты содержательные (не no-throw), раннеры/нейминг по конвенциям пакетов, contract-тесты (SHARED_TOOL_SPECS parity, SSRF-guard) валидны. Открыто: F1 (suggestion, simplification — дублированная ProseMirror table-фикстура в 3 новых файлах, вынести в общий хелпер).
agent_reviewer added the review/changes-requested label 2026-06-29 05:13:56 +03:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin test/244-part-b:test/244-part-b
git checkout test/244-part-b
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#257