Files
docmost-sync/TESTING.md
vvzvlad b03eb353c1
Some checks failed
Test and publish image / test (push) Has been cancelled
Test and publish image / build (push) Has been cancelled
docs: add TESTING.md (how to test docmost-sync)
Root-level testing guide: prerequisites (Node 20+, system git, .env), the offline
test suite + coverage, the §11 round-trip idempotency harness (--fixture/--corpus/
--page), and live testing against a real Docmost — read-only pull, dry-run push
(plan only) vs --apply (writes), vault git inspection, what's implemented vs not,
safety/recovery (Trash is reversible, conflicts never reach Docmost), and a
troubleshooting table.
2026-06-21 02:31:51 +03:00

14 KiB

Как тестировать docmost-sync

Гайд по проверке проекта — от быстрых офлайн-тестов (без Docmost) до живого прогона против реального инстанса. Дизайн и термины — в SPEC.md, конвенции — в AGENTS.md.

TL;DR. make install && npm test — весь набор без всякой сети. Идемпотентность конвертера — npm run roundtrip. Живой синк: заполнить .env, затем npm run pull (read-only из Docmost) и npm run push (dry-run — план без записи; запись только с -- --apply).


0. Пререквизиты

  • Node ≥ 20 и npm.
  • Системный git на PATH — это state store (SPEC §5); движок шеллится в git. Без него движок честно падает с понятным сообщением (а не сырым стэком).
  • Установка зависимостей (монорепо npm-workspaces): make install (или npm ci).
make install     # npm ci: ставит корень + packages/docmost-client
npm run build    # собирает либу, затем движок -> build/

Для живых прогонов (pull/push/--page) нужен .env:

make env         # cp .env.example .env (если ещё нет), затем впишите значения

Переменные (.env, читаются через src/settings.ts на zod; отсутствие обязательной — падение на старте с указанием имени):

Переменная Обяз. Что это
DOCMOST_API_URL да URL API инстанса, напр. https://docs.example.com/api
DOCMOST_EMAIL / DOCMOST_PASSWORD да креды для /auth/login
DOCMOST_SPACE_ID да какое пространство зеркалить
VAULT_PATH нет git-vault, по умолчанию data/vault (gitignored)
GIT_REMOTE нет git-remote для пуша vault (пока не используется)
POLL_INTERVAL_MS, DEBOUNCE_MS, LOG_LEVEL нет тюнинг (дефолты разумны)

.env и data/ — в .gitignore; реальные креды не коммитить (SPEC §12).


1. Автотесты (офлайн, без Docmost)

Весь набор — детерминированный, без сети, без живого инстанса.

npm test            # vitest run — ~747 тестов (2 skipped — это live-e2e, см. §4.5)
make test           # то же через Makefile
npm run test:watch  # watch-режим
npm run coverage    # v8-покрытие по src/ и packages/docmost-client/src/

Что покрыто (по слоям):

  • Чистая логика (high-ROI): конвертер ProseMirror↔Markdown (golden + property), каноникализация (canonicalize), реконсиляция pull/push (reconcile, compute-pull-actions, compute-push-actions, classify-rename-moves), node-ops, transforms, diff, sanitize/layout, settings.
  • git-слой (VaultGit) — интеграционные тесты на временных репозиториях под os.tmpdir() (init/branches/commit-провенанс/merge/ff/diffNameStatus/refs).
  • REST-клиент — через axios-mock-adapter (биндинги delete/restore/trash/ move/recent и т.д.), без реальной сети.
  • Collab-путь записи — через настоящий Y.Doc (без Hocuspocus-сервера).
  • Оркестрация pull/push — через инъецированные fakes; плюс run-push-realgit гоняет --apply-путь против НАСТОЯЩЕГО VaultGit (ловит регрессии биндинга).

Тесты — это и есть «контракт». Падающий npm test блокирует docker-сборку (CI).


2. Идемпотентность round-trip (SPEC §11 / «Задача №0»)

git диффает побайтово: если export → import → export недетерминирован, каждый pull рождает ложный дифф. Harness проверяет это офлайн.

npm run build

# Один фикстур (по умолчанию test/fixtures/sample-doc.json):
node build/roundtrip.js --fixture test/fixtures/sample-doc.json

# Весь синтетический корпус (заголовки, марки, списки, таблицы, callout'ы,
# код с хвостовым \n, диаграммы, mention/textStyle):
node build/roundtrip.js --corpus            # default dir test/fixtures/corpus
npm run roundtrip -- --corpus               # то же через npm-скрипт

Вывод: для каждого файла — md=ok canon=ok. Гарантии:

  • markdown byte-stable (md1 === md2) — свойство, нужное git;
  • canonically stable — семантическое равенство со снятыми block-id и нормализованными дефолтами схемы.

Код выхода: 0 — всё стабильно (CI-able), 1 — найдено расхождение (печатает первую дивергенцию). Известные ограничения конвертера зафиксированы честно через it.fails (блочная картинка между блоками; code-марка с другой маркой).

Живой вариант (реальная страница, нужен .env):

node build/roundtrip.js --page <pageId>

3. Живой pull — Docmost → vault (read-only к Docmost)

pull зеркалит сконфигурированное пространство в локальный git-vault. К Docmost он строго read-only (только читает) — безопасно гонять против боевого инстанса.

# заполните .env, затем:
npm run pull            # = node build/pull.js
make pull

Что происходит (SPEC §6, Docmost→ФС):

  1. ensureRepo создаёт vault-репо (data/vault) с ветками main и docmost.
  2. Обход дерева пространства (/pages/sidebar-pages) + выгрузка тел (без комментариев, только якоря, §3) → запись .md в дерево папок Space/…/Title.md.
  3. Коммит на ветке docmost → merge в main.

Проверить результат:

ls -R data/vault                                   # дерево статей в Markdown
git -C data/vault log --oneline --all              # коммиты docmost:/local:
git -C data/vault branch                           # main + docmost
git -C data/vault show refs/docmost/last-pushed    # (появится после push)
cat data/vault/<Space>/<…>/<Title>.md              # meta-блок + тело + якоря

Защиты: при неполном обходе дерева удаления подавляются (§8), есть порог на массовое удаление, и стартовый guard на незавершённый merge (§9/§12).


4. Живой push — vault → Docmost (запись!)

push транслирует локальные правки .md обратно в Docmost. Это единственный путь записи в Docmost, поэтому он dry-run по умолчанию.

4.1 Dry-run (безопасно — план без записи)

npm run push            # = node build/push.js — DRY-RUN: только печатает план

Dry-run ничего не пишет в Docmost и не двигает рефы. Он печатает план: что будет create / update / delete / move / rename / noop / skipped. Важно: чтобы посчитать дифф base..main, dry-run локально коммитит ожидающие правки рабочего дерева на ветке main (это локальный git, в Docmost ничего не уходит — об этом есть строка в логе).

Типичный сценарий проверки:

npm run pull                      # 1. зеркалим пространство в vault
# 2. правим/добавляем/переименовываем/удаляем .md в data/vault руками
npm run push                      # 3. смотрим план (dry-run) — что уедет в Docmost

4.2 Apply (реальная запись в Docmost)

npm run push -- --apply           # ВЫПОЛНЯЕТ план: пишет в Docmost

Что делает (SPEC §6, ФС→Docmost):

  • modifiedimport_page_markdown через collab/Yjs-путь (не сырой jsonb-overwrite, §2);
  • added без pageIdcreate_page, присвоенный pageId пишется обратно в meta;
  • deleteddelete_page — это soft-delete в Trash Docmost (обратимо, §8);
  • renamed/movedmove_page / rename_page (истина положения — путь файла, §5; чистое локальное переименование без смены родителя/title — noop, в Docmost не транслируется).

После чистого применения двигаются refs/docmost/last-pushed и ветка docmost (loop-close, §6.3/§10) — чтобы следующий pull не утянул свою же запись обратно.

4.3 Безопасность записи

  • Начинайте с dry-run и читайте план.
  • Тестируйте на отдельном/тестовом пространстве, а не на боевом.
  • Удаления обратимы: уходят в Docmost Trash (восстановление — Trash в UI; авто- чистка ~30 дней). Локально всё восстановимо из git-истории vault.
  • Конфликт-маркеры никогда не уезжают в Docmost (§9): при незавершённом merge push прерывается с понятным сообщением.
  • Рекомендуется один авторитетный прогон/демон на пространство (не пушить вдвоём).

4.4 Проверить, что dry-run действительно ничего не пишет

node build/push.js                # без --apply: клиент Docmost даже не строится

(Это же гарантирует автотест: dry-run делает ноль вызовов клиента и ноль движений рефов; путь записи достижим только с --apply + реальными кредами.)

4.5 Живой e2e-smoke

test/e2e-docmost.test.ts — 2 теста, пропущены без реальных кред (поэтому в npm test всегда «2 skipped»). Это сквозной дым против живого инстанса; включается при наличии .env (см. начало файла теста).


5. Что уже реализовано, а что — нет

Чтобы тестировать в границах реального:

Готово и проверяемо:

  • pull (Docmost→vault) — цикл §6 с реконсиляцией и guard'ами безопасности;
  • push (vault→Docmost) — полное покрытие типов изменений (create/update/delete/ move/rename/noop), runnable npm run push (dry-run / --apply), loop-close;
  • идемпотентный round-trip (§11), git-слой, REST-биндинги, collab-запись.

Ещё НЕ реализовано (ручной прогон команд, не демон):

  • непрерывный FS-watcher + дебаунс (§7.1) — сейчас push запускается вручную;
  • поллинг-цикл pull — сейчас pull одноразовый;
  • git push в remote (§7.2) — vault пока локальный;
  • точная fractional-index позиция при move (пока серверный дефолт);
  • потребление loop-guard-записи на pull-стороне (git-native ff-docmost уже закрывает контентную петлю).

Иными словами: обе стороны синка работают как одноразовые команды; непрерывного демона ещё нет.


6. Траблшутинг

Симптом Причина / что делать
git binary not found … поставьте системный git (нужен для vault).
Configuration error … Missing required variable(s) заполните .env (см. §0).
vault has an unresolved merge … в vault остался конфликт: git -C data/vault merge --abort (или разрешите), затем повторите (§9/§12).
pull: tree fetch incomplete — deletions suppressed часть дерева не подтянулась; удаления намеренно не применены — повторите pull (§8).
push «всё-или-ничего» при сбое страницы при ошибке любой страницы рефы НЕ двигаются → повторный прогон чистый (§12).
WARNING — the 'docmost' mirror branch DIVERGED кто-то писал в ветку docmost руками (нарушение §5) — она пишется только движком.
round-trip exit 1 конвертер недетерминирован на этом контенте — см. вывод первой дивергенции (§11).

Полезное:

npm run build && npm test                       # быстрый sanity всего
node build/roundtrip.js --corpus                # идемпотентность
git -C data/vault log --oneline --all --decorate
make clean                                      # снести build/ + node_modules + dist либы