* fix(draw-image): per-process temp dir in renderToPng test helpers * docs(handoff): add handoff and ADR for renderToPng temp dir fix * docs(project-map): note per-process temp dir fix in renderToPng helpers * docs(handoff): set PR number * docs(handoff): replace PR number placeholder in frontmatter --------- Co-authored-by: opencode-agent <agent@opencode.local>
3.5 KiB
ADR-080: Per-process temp dir in renderToPng test helpers
Статус
Accepted (2026-07-31)
Контекст
Тестовые хелперы renderToPng в tests/render.integration.test.ts и tests/render.optional.integration.test.ts рендерили SVG через двухфазный процесс:
writeFileSync(path.join(TMP, "input.svg"), svg)— запись в единственный fixed-path файл в общемTMP-каталоге (/tmp/draw-image-integration, общий для всех параллельных vitest-процессов).spawnSync("node", ["render.mjs", tmpSvg, outPath])— дочерний процесс читает файл и рендерит PNG через sharp.
При параллельном запуске нескольких vitest-процессов (внешняя параллель — npx vitest run × N concurrently) процессы разделяли один input.svg: один процесс обнулял/перезаписывал файл, пока другой его читал → render.mjs падал с Input Buffer is empty. Это тестовый аналог бага #160 (продакшн cli.ts), но в хелпере, а не в CLI. Воспроизведено: 10 параллельных прогонов → run 9 упал с Input Buffer is empty.
Решение
Использовать уникальный temp-каталог на каждый вызов renderToPng через mkdtempSync под системным os.tmpdir() с PID в префиксе:
const tmpDir = mkdtempSync(path.join(os.tmpdir(), `draw-image-test-${process.pid}-`))
const tmpSvg = path.join(tmpDir, "input.svg")
try {
writeFileSync(tmpSvg, svg)
// ... spawnSync render.mjs ...
} finally {
if (existsSync(tmpDir)) rmSync(tmpDir, { recursive: true, force: true })
}
и удалять каталог в finally-блоке после рендера (cleanup гарантирован даже при throw). render.mjs не изменён — он уже принимает path к SVG как argv, уникальность обеспечивает хелпер.
Альтернативы
- PID-суффикс на файл вместо каталога:
path.join(os.tmpdir(), \draw-image-test-input-${process.pid}.svg`)— как вcli.ts(#160). Достаточен для race между процессами, но НЕ решает race между threads/воркерами одного vitest-процесса (одинprocess.pid, несколько параллельных вызововrenderToPng).mkdtempSync` даёт уникальность на каждый вызов, закрывая оба случая (граничный случай из спеки #177). - Уникальный каталог на процесс без
mkdtempSync:path.join(os.tmpdir(), \draw-image-test-${process.pid}`)+mkdirSync— требует ручной гарантии уникальности и cleanup;mkdtempSync` атомарно создаёт уникальный каталог за один вызов. - Передача SVG через stdin в
render.mjs: избегает temp-файла целиком, но требует переписыванияrender.mjs(чтение из stdin вместоreadFileSync(argv)), что выходит за рамки спеки #177 и ломает контракт «render.mjsпринимает path как argv».