Files
admin/.wiki/concepts/windows-docker-test-harness-gotchas.md

76 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Windows docker-тест-харнесс: грабли верификационных скриптов"
type: concept
tags: [windows, testing, node, docker, vitest, sqlite, gotchas]
sources: []
related: [concepts/sched-fnf-r8-verification-stand.md]
updated: 2026-08-18
---
# Windows docker-тест-харнесс: грабли
Собрано из верификационных прогонов против docker-контейнеров на Windows-хосте
(стенд sched F&F r8, 2026-08-18). Переиспользуемо для любой проверки
«локальные репро-скрипты против живых docker-БД».
## 1. `rmSync` удаляемого sqlite-файла — EBUSY на Windows
`new DatabaseSync('x.db')` создаёт файл, и `rmSync('x.db')` ПОСЛЕ открытия падает
`EBUSY` (errno -4082): Windows не удаляет открытый файл (на POSIX unlink
открытого — ок, на Windows — нет).
**Правило: `rmSync` ДО `DatabaseSync`.** Порядок в тесте:
```js
rmSync('x.db', { force: true });
const db = new DatabaseSync('x.db');
```
Симптом-ловушка: тот же EBUSY бывает, если файл держит ВИСЯЩИЙ node-процесс
прошлого прогона (см. п.4) — сначала убить процессы, потом чинить порядок.
## 2. vitest 10s-таймаут vs медленный docker-DDL
На этом хосте DDL в docker-mysql8 стоит ~2001500ms на запрос (CREATE TABLE,
ALTER), а `createMysqlStorage` (миграции v1→v5 на свежей БД) — ~11s. vitest-дефолт
10s на тест → каждый mysql-тест падает в `beforeEach` по таймауту, БЕЗ
assertion-ошибок. Выглядит как регрессия, а это окружение.
Варианты: `--testTimeout=60000`, либо (лучше для стенда) не гонять тяжёлый
contract-сьют на mysql/pg/mariadb через vitest — покрывать те же пути
отдельными node-скриптами (`adapters-spotcheck`, `adapter-migration-test`).
## 3. Фейк-часы в тестах движка — монотонность и изоляция
Инжектируемый `now()` даёт детерминизм, но:
- **Часы должны идти МОНОТОННО.** Прыжок назад (например, после тика 10:03 гнать
once-расписание на 10:01) ломает: расписание «уже» выстрелило catch-up'ом на
более позднем тике, nextRunAt=null. Планируй все времена по возрастанию.
- **Отдельный DB на сценарий.** Общий DB + прыгающие часы → соседняя задача
становится due на «чужом» тике и стреляет (мешает счётчикам/онRunFinal).
- **`rmSync` в начале каждого прогона** — иначе повторный запуск видит старые
раны (флак «запусти ещё раз — упало»).
- **Блокирующий раннер + `runOnce`** = дедлок, если release после `await`.
Тик не await'ить, дать микро-паузу на claim, release, потом `await`.
## 4. Висельники node-процессов держат файлы/порты
После таймаутов и убитых прогонов node-процессы живут и держат sqlite-файлы
(→ EBUSY) и соединения. `tasklist | grep node` врёт (UTF-16 binary); правильно:
```powershell
Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Select ProcessId,CommandLine
```
Убивать точечно по CommandLine (репо-путь), НЕ всех — рядом крутятся dev-серверы
юзера (nuxt, pi-агенты, wiki-graph). Таймаут bash убивает шелл, но не дерево
node-детей — чистить руками.
## 5. Секвенции assert'ов: «ожидание» vs «поведение»
Половина «багов» в репро-скриптах — неверные ожидания в тесте, а не движок:
- кумулятивные счётчики (retryCount = consumed-попытки, не «текущая»);
- тай-брейки сортировок (priority-равные → id ASC);
- событийные хуки на терминальных ранах (success тоже);
- легаси-форматы данных (mongo: `config.data` не `data`, `paused`/`disabled`
обязательны — иначе синтез даёт null и строка не в due).
Перед репортом о «находке» — перепроверить, что ожидание соответствует доке
(это часто и есть ответ: поведение документировано, тест был неверен).