Files
gitmost/docs/backlog/feature-test-coverage-deferred.md
claude_code 41efacbe3d docs(backlog): move non-test findings out to issues #52-#56
Keep the backlog focused on deferred TESTS; the related non-test gaps
(model-allow-list, restriction-cache invalidation, server embed-recursion
guard, collectPageEmbeds cycle guard, jest DI/lib0-ESM debt) are now
tracked as issues #52-#56 and only linked from the backlog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 00:25:05 +03:00

6.4 KiB

Отложенные тесты по фичам с коммита 053a9c0d (хвост от PR #49)

Контекст

PR #49 («test: cover features since 053a9c0d + repair test tooling») закрыл основную массу покрытия новых фич gitmost (+~330 тестов: server/Jest, client/Vitest, editor-ext/Vitest, packages/mcp/node:test) и починил тест-инструментарий (FIX-0 сломанные спеки transclusion, BUILD-0 сборка editor-ext перед серверными тестами, INFRA-0 резолв .tsx email-шаблонов).

Часть тестов из принятого тест-плана намеренно отложена — им нужен тестовый Postgres, реальный Redis или HTTP/e2e-харнес, которых в проекте сейчас нет, либо инвазивный рефактор продакшн-кода. Ниже — что осталось и почему, чтобы не потерять.


1. Интеграционные тесты против БД (нужен тестовый Postgres)

Сейчас все repo-зависимые проверки делаются на моках; SQL-уровень не исполняется. Чтобы покрыть это честно, нужен поднимаемый в CI Postgres (testcontainers или сервис в pipeline) + хелпер миграций.

  • AiAgentRoleRepo — изоляция и индексы. apps/server/src/database/repos/ai-agent-roles/ai-agent-roles.repo.ts. Проверить против реальной БД: findById/listByWorkspace исключают soft-deleted строки; findById для roleId из ЧУЖОГО workspace → undefined (tenant-изоляция); дубль имени в одном workspace → 23505; то же имя переиспользуемо после softDelete (partial unique index WHERE deleted_at IS NULL, миграция 20260620T120000-ai-agent-roles.ts); одинаковое имя в разных workspace разрешено. Это «хребет» безопасности — сейчас только предполагается unit-моками.

  • AiChatRepo.findByCreator — join role-badge. apps/server/src/database/repos/ai-chat/ai-chat.repo.ts (~:27-70). Чат с enabled-ролью → roleName/roleEmoji заполнены; с soft-deleted ролью → бейдж NULL; с DISABLED ролью → бейдж NULL (должно совпадать с resolveRoleForRequest); ORDER BY квалифицирован aiChats.* (нет ambiguous column после join). Не проверяемо чистым unit-ом.

  • WorkspaceService.update / WorkspaceRepo.updateSetting — jsonb-merge. apps/server/src/core/workspace/services/workspace.service.ts (:514), apps/server/src/database/repos/workspace/workspace.repo.ts (:275). Сейчас покрыта только форма вызова сервиса (workspace-html-embed.spec.ts). Не покрыто (нужна БД): htmlEmbed:true персистится через jsonb-merge не затирая соседние настройки (ai, sharing). Это и есть «kill-switch пишется» — критично, что write-половина тоггла не ломает остальной settings-namespace.

  • FK page_template_references onDelete('cascade'). Миграция 20260620T131000-page-template-references.ts. Проверить, что удаление source/reference-страницы каскадит строки ссылок.

2. HTTP / e2e-харнес (его нет в apps/server)

  • Public-share ассистент: обход per-IP throttle ротацией XFF, но per-workspace cap держит. Контроллер использует стоковый @UseGuards(ThrottlerGuard) (apps/server/src/core/ai-chat/public-share-chat.controller.ts), IP берётся из Fastify trustProxyX-Forwarded-For. Единственный оправданный e2e (named journey «аноним спамит ассистента»): ротация XFF обходит per-IP лимит 5/min, но per-workspace cost-cap всё равно отдаёт 429. Требует поднятого HTTP-слоя Nest + trusted-proxy конфигурации.

  • Достоверность Lua-окна cost-cap против реального Redis. apps/server/src/core/ai-chat/public-share-workspace-limiter.ts (SLIDING_WINDOW_LUA). Сейчас cap тестируется против TS-реализации FakeRedis в public-share-chat.spec.ts — баг в самой Lua-строке (>= vs >, неверный PEXPIRE) не поймается. Нужен интеграционный тест против реального/testcontainers Redis.

3. Полная интеграция AiChatService.stream (рефактор R1-stream)

apps/server/src/core/ai-chat/ai-chat.service.ts. В PR #49 извлечён и покрыт только чистый buildErrorAssistantRecord. Полные интеграционные сценарии — запись чата, упавшего на первом ходу (onError), жизненный цикл external-MCP клиентов (закрытие при throw/onFinish), и история восстанавливается из БД, а не из body.messages (анти-tamper) — требуют сидирования SDK streamText (инъекция/seam колбэков onError/ onFinish/onAbort + res.hijack). Отложено, чтобы не дестабилизировать 287-строчный stream(); делать вместе с выносом testable turn-pipeline.


Сопутствующие НЕ-тестовые находки

Вынесены в отдельные issues (всплыли во время написания тестов):

  • #52 — ai-roles: нет серверной валидации модели роли + дрейф enum драйверов.
  • #53 — ws: invalidateSpaceRestrictionCache без вызывающих (30с stale-окно).
  • #54 — page-embed: серверный guard глубины/циклов раскрытия.
  • #55 — transclusion: cycle-guard в collectPageEmbedsFromPmJson.
  • #56 — test-infra: jest DI + lib0 ESM (16 падающих сьютов).