* 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>
33 lines
No EOL
3.5 KiB
Markdown
33 lines
No EOL
3.5 KiB
Markdown
# 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 через двухфазный процесс:
|
||
1. `writeFileSync(path.join(TMP, "input.svg"), svg)` — запись в единственный fixed-path файл в общем `TMP`-каталоге (`/tmp/draw-image-integration`, общий для всех параллельных vitest-процессов).
|
||
2. `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 в префиксе:
|
||
```ts
|
||
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». |