From b630a5ccac3f26402384cd72cd1aceab11653f71 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Wed, 17 Jun 2026 01:35:16 +0300 Subject: [PATCH] docs: delete test strategy report Remove the large `test-strategy-report.md` file which is outdated and has been superseded by newer testing documentation, reducing repository clutter. --- test-strategy-report.md | 248 ---------------------------------------- 1 file changed, 248 deletions(-) delete mode 100644 test-strategy-report.md diff --git a/test-strategy-report.md b/test-strategy-report.md deleted file mode 100644 index eddbf72..0000000 --- a/test-strategy-report.md +++ /dev/null @@ -1,248 +0,0 @@ -# Отчёт по тест-стратегии — docmost-sync — 2026-06-16 - -> Двунаправленная синхронизация статей Docmost с локальным Markdown-git-хранилищем -> (git — хранилище состояния). Монорепо: корневое приложение-движок (`src/`) + -> библиотека `packages/docmost-client` (~7.5k LOC). Стек: TypeScript ESM, Node ≥ 20, -> Vitest 3.2.6. Все тесты лежат в корневом `test/` (`include: ['test/**/*.test.ts']`). - -## 1. Исполнительное резюме - -- **Проанализировано модулей:** 9 (1 субагент `module-testability-analyst` на модуль, все завершились). -- **Предложено тестов (unit / integration / contract / E2E):** **50 / 7 / 1 / 2** (итого 60). - - unit = 83 % (≥ 70 % ✓), integration = 12 % (≤ 20 % ✓), E2E = 3 % и 2 шт. (≤ 5 % и ≤ 10 ✓). -- **Отклонено как малоценные:** ≈ 60 символов/областей (декларативные spec-объекты схемы, - тривиальные плоские мапперы, framework-обвязка, type-only интерфейсы, passthrough-обёртки). -- **Покрытие сейчас (проверено v8 лично):** **2.6 %** statements по обоим пакетам - (искажено огромным непокрытым `docmost-client`). Изолированно: корневое приложение ≈ **40 %**, - пакет `docmost-client` ≈ **0 %** (поведенчески покрыт лишь `collectRecentSince`). - **Прогноз после Фаз 1–4:** ≈ **60–65 %** (чистые lib-модули 80 %+, корневое приложение ≈ 85 %, - транспортный `client.ts` ≈ 40 %). - -> ⚠️ **Артефакт измерения покрытия.** `package.json` пакета указывает `main: dist/index.js`, -> поэтому `import from 'docmost-client'` грузит **скомпилированный `dist/`**, а не `src/`. -> v8 меряет `src/` → показывает `client.ts` 0 %, хотя `collectRecentSince` реально исполняется. -> **Перед измерением покрытия** добавить в `vitest.config.ts` alias `docmost-client → packages/docmost-client/src/index.ts` -> (или мерить по `dist` после сборки), иначе любые новые тесты библиотеки не отразятся в отчёте. -> `@vitest/coverage-v8` и скрипт `"coverage"` в проекте отсутствуют — их нужно добавить. - -## 2. Рекомендации по модулям - -### app-root (`src/`) — движок синка, конфиг, sanitize, round-trip-харнесс -- **Извлечь в чистые функции:** `folderSegmentsFor` (`pull.ts:88`, замкнута внутри `main`), - `firstDivergence`/`parseArgs` (`roundtrip.ts:101/64`, не экспортированы). -- **Unit добавить:** `firstDivergence` (равные/разные деревья, путь расхождения, циклы) — - ловит ложное «stable» при реальном расхождении (вся суть харнесса); `nameForNode` (коллизии - имён сиблингов → перезапись файлов на диске); `folderSegmentsFor` (вложенность + защита от цикла - parent A→B→A, иначе зависание); `parseArgs`; ветка invalid-value в `loadSettingsOrExit` - (`config-errors.ts:27-30`, единственный значимый пробел). -- **Integration добавить:** `pull.main` с фейковым клиентом + временной директорией - (один файл на страницу, верные папки, узлы без id пропускаются) — после R-App-4. -- **НЕ тестировать:** `index.ts` (тонкий CLI-passthrough, только `console.log`); `envSchema` - (тестировать = тестировать Zod, покрыт через `parseSettings`); тело `roundtrip.main` - (байт-стабильность уже покрыта `roundtrip.test.ts`); `invokedDirectly`-guard-блоки; - `sanitizeTitle`/`disambiguate`/`parseSettings`/`stripBlockIds` (уже ~100 %). - -### client-core (`packages/docmost-client/src/client.ts`, ~2770 строк) — god-object REST+WS клиент -- **Извлечь в чистые функции:** валидаторы `isSafeUrl`/`validateDocUrls`/`validateDocStructure` - (`client.ts:905/941/1004`), `imageMimeFromPath`/`buildImageNode` (1844/1864) — поднять в `lib/` - рядом с `filters.ts`; распаковку конвертов и clamp-логику пагинации (378-393, 1505) в pure-функции. -- **Unit добавить:** XSS-allowlist `isSafeUrl`+`validateDocUrls` (`javascript:`/`data:`/`file:`, - пробельно-контрольный обход `java\tscript:`, на всех медиа-узлах) — **высший приоритет по безопасности**; - `validateDocStructure` (глубина > 200, не-string type); расширить `collectRecentSince` - (граница `updatedAt === sinceIso`, элементы без `id`/`updatedAt`); `imageMimeFromPath`+`buildImageNode`; - `paginateAll` (стоп-условия, MAX_PAGES=50 + предупреждение, clamp 1..100, оба конверта) — после R-Client-2; - `appUrl`/`shareUrl`/`parseCommentContent`; sandbox `transformPage` (`node:vm`: нет `require`/`process`/`fs`, - таймаут 5 c, не-функция/не-doc → throw) — security. -- **Integration добавить (после R-Client-1, инъекция HTTP):** авто-реавторизация - (401-интерсептор + дедуп `login` + `getCollabTokenWithReauth`: один retry, `/auth/login` не ретраится, - `loginPromise` сбрасывается в `finally`); `uploadImage` (порядок guard ext→stat→read, > 20 MiB, - пересборка FormData на 401, нет утечки тела ответа в ошибку); `createPage` (replay multipart на 401); - `checkNewComments` (битая дата → throw, а не «ничего нового»; граница `createdAt > since`; флаг truncated). -- **НЕ тестировать:** тонкие REST-passthrough (`getWorkspace`/`getSpaces`/`renamePage`/`movePage`/ - `deletePage`/`restorePage`/`listTrash` и пр.) — конверт `data.data ?? data` покрыть один раз - извлечённой функцией; делегаты в node-ops/converter/diff (тестировать в их модулях); сами axios/yjs/hocuspocus. - -### markdown-conversion (`lib/markdown-converter.ts` + `markdown-document.ts`) — конвертер ProseMirror↔Markdown -- **Unit добавить:** табличная golden-матрица по типам узлов (заголовки, маркированные/кодовые - спаны, ссылки с title, картинки с пробелами/скобками в src, кодоблоки с языком + срез хвостовых `\n`, - GFM-таблицы с выравниванием, spanned-таблицы → ``, blockquote, task-list, math `a < b`, - mention/attachment/callout/details/columns/медиа, hr, hard break, неизвестный тип, пустой doc → `""`); - идемпотентность экранирования (`escapeAttr` стабилен на `& "`, `encodeMdUrl` пробел→`%20`), - отступы вложенных списков (`indentItemChildren`); envelope `parseDocmostMarkdown`/`serializeDocmostMarkdown` - (восстановление meta/body/comments, CRLF, «последний `docmost:comments`-блок побеждает», throw на битом JSON); - edge/malformed-вход (`null`/`{}`/нет content, отсутствующие attrs, глубокая вложенность без переполнения стека). -- **Integration добавить:** **property-тест round-trip идемпотентности** — `md→PM→md == md` байт-в-байт - + семантическая стабильность через `stripBlockIds`. **Самый ценный тест проекта** (фантомные git-диффы — - ровно то, ради чего существует харнесс). Требует фабрику документов и генератор (см. §3). -- **НЕ тестировать:** интерфейс `DocmostMdMeta`; одиночный токен `{{SUBPAGES}}`; внутренности - `marked`/`@tiptap/html`; underline/sub/sup как отдельные тесты — свернуть в один inline-marks-кейс. - -### prosemirror-schema (`lib/docmost-schema.ts`, ~1065 строк) — ~90 % декларативный конфиг -- **Unit добавить (ровно 2, намеренно не раздуваем):** `sanitizeCssColor` (`:44`) — allowlist против - CSS/style-инъекции: принять named/hex3-8/rgb(a)/hsl(a), отвергнуть `red; --x:url()`, `expression(...)`, - `red">