Кандидаты на юнит-тесты (из gap-аудита QA-плана PR #136) #139
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Чеклист (10 функций)
encodeWavPcm16— WAV-кодек потоковой диктовкиgetEmbedUrlAndProvider/getEmbedProviderById— провайдеры эмбедовsanitizeUrl/isInternalFileUrl— XSS-граница ссылок/эмбедовclampIndent— отступы блоков (Tab/Shift-Tab)/^[a-z0-9_-][a-z0-9_~-]*$/MovePageDto.positionпротивgenerateJitteredKeyBetweencanCreatePage— гейт кнопки «New note»getTypesForTabпротив запроса репозитория уведомленийto_tsquery(поиск)Кандидаты на юнит-тесты (из gap-аудита QA-плана PR #136)
Статус: открыто. Источник — gap-аудит «забытых кейсов» ручного QA-плана
(PR #136, ветка
docs/manual-qa-test-plan). Ниже — чистые/почти-чистыефункции, которые сейчас проверяются только вручную (или не проверяются вовсе).
Каждую дешевле и надёжнее покрыть юнит-тестом, а не ручным прогоном — это
единственный «пирамидный» сдвиг из всего аудита. Записи самодостаточны: target,
файл:строка, что ассертить (конкретные векторы), какой класс дефектов ловит.Каждый пункт — отдельный
*.test.ts/*.spec.tsрядом с целевым файлом(фронт —
*.test.tsчерез vitest, сервер —*.spec.tsчерез jest, как принято в проекте).1.
encodeWavPcm16— WAV-кодек потоковой диктовкиapps/client/src/features/dictation/utils/encode-wav.ts:3-32(чистая, без зависимостей).RIFF/WAVE/fmt/data;fmtsize = 16; audioFormat = 1 (PCM); channels = 1; bitsPerSample = 16;byteRate = sampleRate*2;blockAlign = 2;dataSize = samples.length*2.+1.0 → 0x7FFF (32767),-1.0 → -32768,0 → 0,+1.5/-1.5→ рельсы; проверить асимметриюclamped < 0 ? *0x8000 : *0x7fff(нет переполнения ровно на −1.0).44 + samples.length*2.2.
getEmbedUrlAndProvider/getEmbedProviderById— провайдеры эмбедовpackages/editor-ext/src/lib/embed-provider.ts:8-142(нет теста)./embed/-URL и мусорный URL:watch?v=,youtu.be/,m./music.→youtube-nocookie.com/embed/<id>.player.vimeo.com/video/<id>.{22,128}; gdrive/gsheets индексmatch[4].{ provider: "iframe", embedUrl: url }(ветка Iframe).3.
sanitizeUrl/isInternalFileUrl— XSS-граница ссылок/эмбедовpackages/editor-ext/src/lib/utils.ts:385-398(нет теста). Потребители:embed.ts, link, attachment, pdf, audio.hrefэмбеда/ссылки/вложения.sanitizeUrl("javascript:alert(1)")→""(обёртка маппитabout:blankбиблиотеки в пустую строку).data:/vbscript:→"";https://…, относительный/api/files/…,mailto:→ проходят.isInternalFileUrlистинно только для префиксов/api/files/и/files/после trim.4.
clampIndent— отступы блоков (Tab/Shift-Tab)packages/editor-ext/src/lib/indent.ts:36-83(нет теста; в плане вообще нет TC на indent).data-indent(non-finite / negative / >8) → clamp в min/max.5. Регекс имени метки
/^[a-z0-9_-][a-z0-9_~-]*$/apps/server/src/core/label/dto/create-label.dto.ts(полеname,@Matches(...)).normalizeLabelNameуже покрыт — НЕ дублировать.foo,a~b,1-2_3,-lead; reject~lead(тильда первой),a b(пробел),héllo(unicode), пустое. Зафиксировать:~допустим только НЕ первым символом.6.
MovePageDto.positionпротивgenerateJitteredKeyBetweenapps/server/src/core/page/dto/move-page.dto.ts:14-16(@MinLength(5) @MaxLength(12)) vs генератор ключаapps/server/src/core/page/services/page.service.ts:911.generateJitteredKeyBetween(null,null)="a0"(длина 2 < 5) → DTO бы отверг; серия плотных вставок between даёт ключи >12 → тоже отверг. Тест фиксирует диапазон длин ключа против границ DTO (5..12) и доказывает несовместимость → блокирует баг до фикса границ.7.
canCreatePage— гейт кнопки «New note»apps/client/src/features/home/components/new-note-button.tsx:17-20(чистый предикат).ADMIN/WRITER; false дляREADER; пустой список space → false.8. diff → декорации + счётчики (история версий)
apps/client/src/features/page-history/components/history-editor.tsx:36-174— вынести вычисление «old/new ProseMirror JSON → decorationSet + added/deleted/total» в чистую функцию и протестировать.9.
getTypesForTabпротив запроса репозитория уведомленийapps/server/src/core/notification/notification.constants.ts:49-53(нет теста; не используется репозиторием).getTypesForTab('direct')= ровно 5 whitelisted типов;'updates'=[PAGE_UPDATED]. Плюс контракт-тест: запрос репо (notification.repo.ts:58, сейчасtype != PAGE_UPDATED) должен совпадать с whitelist — тест зафиксирует расхождение как падающий.10. Построитель
to_tsquery(поиск)apps/server/src/core/search/search.service.ts:37— вынести трансформtsquery(query.trim()+'*')и протестировать на адверсариальных входах.to_tsquerysyntax error → 500 (см. TC-SRCH-05)."&","!","*","<->","\\","the a of"(стоп-слова), пустое-после-trim, very long, unicode → безопасный пустой/нейтральный tsquery, без выброса. Гардlength<1не покрывает whitespace/операторы — тест это вскрывает.Уже покрыто — НЕ дублировать
normalizeLabelName,resolveAudioFormat, html-embed sandbox,decideEmbedState,статическая матрица collab-auth, persistence/history-job, zip-slip safety,
queue-helpers,chat-markdown(pending),buildPartialAssistantRecord,prepareAgentStep,showTypingIndicator.