test: review-batch-2 follow-up coverage (sandbox html-embed, #101 fixes, i18n) #110

Merged
vvzvlad merged 12 commits from test/review-batch-2-followups into develop 2026-06-21 05:55:11 +03:00

Тест-покрытие, заведённое в ревью PR #101 (смержен) и sandbox-рефактора html-embed. 12 коммитов, по коммиту на issue, всё на актуальном develop (после мержа #101). Тесты/мелкие чистые экстракции, без изменения поведения.

Closes #98, #99, #100, #102, #103, #104, #105, #106, #108, #109

Что покрыто

Sandbox html-embed + trackerHead (#98/#99/#100): оба бага из issues (BUG-1 $&-инъекция trackerHead, BUG-2 height NaN) УЖЕ были починены в sandbox-рефакторе — добавлены тесты, пиннящие инварианты:

  • injectTrackerHead (вынес в чистый хелпер): сниппет с $&/$$/$` вставляется байт-в-байт (падает на старом строковом replacer); empty/whitespace/нет </head> → без изменений.
  • Sandbox-токены ровно allow-scripts allow-popups allow-forms (нет allow-same-origin), рендер через srcDoc; clampHeight [40,4000]; isTrustedHeightMessage (чужой source/тип/NaN/Infinity отвергнуты).
  • editor-ext height codec ("abc"→null — пиннит NaN-guard); mcp htmlEmbed round-trip (source/height); DTO-валидация (@MaxLength(20000), @IsBoolean); CASL-гейт записи trackerHead не-админом; slash-меню гейтинг; no-op аудит-ветка.

Тесты на фиксы из #101: #102 movePage cycle-guard (self-move/cycle/legit), #103 non-text part → 400, #104 (resolveShareAiMaxOutputTokens — уже было), #105 export+тест resolveTrustProxy (вынес из main.ts), #106 reconnect-resync (makeConnectHandler), #108 assistant-name предикат.

i18n #109: ru-RU получил недостающие AI agent / AI agent is typing… (был mixed-language); en-US полный; остальные локали — fallback на en-US (полный перевод — отдельная задача).

Проверка

server 10 suites / 77 tests (затронутые) — все зелёные; client 4 files / 40 tests; server+client tsc --noEmit — чисто. Ветвь от текущего develop, конфликтов нет.

🤖 Generated with Claude Code

Тест-покрытие, заведённое в ревью PR #101 (смержен) и sandbox-рефактора html-embed. 12 коммитов, по коммиту на issue, всё на актуальном develop (после мержа #101). Тесты/мелкие чистые экстракции, без изменения поведения. Closes #98, #99, #100, #102, #103, #104, #105, #106, #108, #109 ## Что покрыто **Sandbox html-embed + trackerHead (#98/#99/#100):** оба бага из issues (BUG-1 `$&`-инъекция trackerHead, BUG-2 height NaN) УЖЕ были починены в sandbox-рефакторе — добавлены тесты, пиннящие инварианты: - `injectTrackerHead` (вынес в чистый хелпер): сниппет с `$&`/`$$`/`` $` `` вставляется байт-в-байт (падает на старом строковом replacer); empty/whitespace/нет `</head>` → без изменений. - Sandbox-токены ровно `allow-scripts allow-popups allow-forms` (нет `allow-same-origin`), рендер через `srcDoc`; `clampHeight` [40,4000]; `isTrustedHeightMessage` (чужой source/тип/NaN/Infinity отвергнуты). - editor-ext height codec (`"abc"`→null — пиннит NaN-guard); mcp htmlEmbed round-trip (source/height); DTO-валидация (`@MaxLength(20000)`, `@IsBoolean`); CASL-гейт записи trackerHead не-админом; slash-меню гейтинг; no-op аудит-ветка. **Тесты на фиксы из #101:** #102 movePage cycle-guard (self-move/cycle/legit), #103 non-text part → 400, #104 (resolveShareAiMaxOutputTokens — уже было), #105 export+тест `resolveTrustProxy` (вынес из main.ts), #106 reconnect-resync (`makeConnectHandler`), #108 assistant-name предикат. **i18n #109:** ru-RU получил недостающие `AI agent` / `AI agent is typing…` (был mixed-language); en-US полный; остальные локали — fallback на en-US (полный перевод — отдельная задача). ## Проверка server 10 suites / 77 tests (затронутые) — все зелёные; client 4 files / 40 tests; server+client `tsc --noEmit` — чисто. Ветвь от текущего develop, конфликтов нет. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 12 commits 2026-06-21 05:53:55 +03:00
Adds the missing tests for the #67 guard: self-move and a destination inside the
moved page's subtree both throw BadRequestException before updatePage; a
legitimate move proceeds. Mocks pageRepo + spies getPageBreadCrumbs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers the #63 guard: a message with a non-text part -> 400 'Unsupported message
content'; a message mixing text + a non-text part still 400s (before the 413
size check).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Relocate resolveTrustProxy from main.ts (untestable — bootstraps on import) to
integrations/environment/trust-proxy.util.ts and import it back. Unit-test every
branch (empty/undefined -> safe loopback/private default; true/false; hop count;
trim; CIDR/negative passthrough) so a regression can't silently re-open the XFF
spoofing hole (#61).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the shared assistant-name predicate (resolveAssistantName: trimmed name
or null) used by typing-indicator + message-item, and unit-test the branches
(name shown; whitespace-only -> 'AI agent' fallback; undefined -> fallback).
Behavior-identical (|| -> ?? since the helper returns null).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the admin trackerHead <head> injection into a pure injectTrackerHead()
and test it: a snippet containing $&/$$/backtick-dollar survives BYTE-FOR-BYTE
(pins the function-replacer fix), empty/whitespace/undefined and a missing </head>
leave the html unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DTO: trackerHead @IsString/@MaxLength(20000) + htmlEmbed @IsBoolean accept/reject
cases. CASL: a non-admin updating trackerHead/htmlEmbed gets ForbiddenException
(update not called); owner/admin proceed. Audit: a no-op trackerHead re-save
doesn't enter the audit diff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add htmlEmbed to the schema toYdoc/fromYdoc acceptance cases, asserting source +
height survive, so removing the passthrough node (which prevents 'Unknown node
type: htmlEmbed' on MCP/AI edits of an embed page) fails CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract clampHeight + isTrustedHeightMessage + the HTML_EMBED_SANDBOX token
constant from the NodeView and test them: clamp bounds; reject a resize message
from a foreign window / wrong type / NaN/Infinity; accept a valid same-source
finite message; assert the sandbox is exactly 'allow-scripts allow-popups
allow-forms' (no allow-same-origin) and rendered via srcDoc (not src).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract parse/renderHtmlEmbedHeight and test: '300'->300, absent->null,
'abc'->null (pins the NaN guard), '120px'->120; render 120->data-height, null/0->{}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Export + test isHtmlEmbedFeatureEnabled: the 'HTML embed' slash item is hidden by
default / when the toggle is off / on broken localStorage (no throw), shown only
when the workspace toggle is exactly true.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ru-RU had only '{{name}} is typing…' but not 'AI agent' / 'AI agent is typing…',
so the Russian typing indicator was mixed-language. Add them (AI-агент / AI-агент
печатает…) grouped with the named key. en-US is already complete; other locales
intentionally keep the en-US fallback (full translation is a separate effort).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract makeConnectHandler(queryClient) (owning the firstConnect flag) from
UserProvider and test it: first connect does NOT invalidate; a reconnect
invalidates both root-sidebar-pages + sidebar-pages. Behavior-identical (#66).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vvzvlad merged commit e5bc82c7f1 into develop 2026-06-21 05:55:11 +03:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#110