* 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.9 KiB
pr: 184 title: fix(draw-image): per-process temp dir in renderToPng test helpers
Что сделано
tests/render.integration.test.ts: хелперrenderToPngпереписан — вместо общегоpath.join(TMP, "input.svg")(гдеTMP = "/tmp/draw-image-integration"— общий для всех параллельных vitest-процессов) используется уникальный temp-каталог на каждый вызов черезmkdtempSync(path.join(os.tmpdir(), \draw-image-test-${process.pid}-`)), внутри которого пишетсяinput.svg`.tests/render.integration.test.ts: добавленfinally-блок сrmSync(tmpDir, { recursive: true, force: true })— temp-каталог удаляется после рендера даже при ошибке/exit.tests/render.integration.test.ts: импорты очищены —require("node:fs")заменён на статическийwriteFileSync/mkdtempSyncизnode:fs, добавленimport os from "node:os".tests/render.optional.integration.test.ts: аналогичный фикс хелпераrenderToPng(тот же паттернTMP/input.svg) — уникальный temp-каталог черезmkdtempSyncс PID + cleanup вfinally.
Почему
Тестовый хелпер renderToPng в обоих integration-тестах писал временный SVG в жёстко заданный path.join(TMP, "input.svg") где TMP — общий каталог для всех параллельных vitest-процессов. При параллельном запуске (CI матрица, npx vitest run × N в параллель, несколько vitest-воркеров) процессы конкурировали за один input.svg: один процесс обнулял/перезаписывал файл через writeFileSync, пока другой его читал через render.mjs → render.mjs падал с Input Buffer is empty. Это аналогично багу #160 (продакшн cli.ts), но в тестовом хелпере. Уникальный temp-каталог на вызов (mkdtempSync с PID в префиксе) + cleanup в finally устраняют race и гарантированно удаляют leftover. mkdtempSync выбран вместо PID-суффикса на файл, т.к. решает и race между процессами, и между threads одного процесса (каждый вызов renderToPng = свой уникальный каталог).
Pending
—
Watch out
- Баг вне scope (создан issue #183): при верификации параллельным запуском 10×
npx vitest runобнаружен отдельный race на общих output-каталогахbeforeAll(/tmp/draw-image-integration,/tmp/draw-image-cleanup,/tmp/draw-image-idempotencyи т.д.) —beforeAllделаетrmSync(TMP, recursive)и процесс удаляет output PNG другого процесса →existsSync(outPath)= false / idempotencyskippedвместоrendered/unable to open for write. Этот race НЕ связан сinput.svg/Input Buffer is emptyи чинится отдельно в #183. Критерий приёмки #177 выполнен:Input Buffer is emptyполностью устранён во всех 10 параллельных прогонах. - Одиночный прогон ×10 стабильно зелёный (58/58 каждый прогон) — проверено последовательно.
render.mjsне изменён (контракт «принимает path к SVG как argv» сохранён).- Продакшн-код
cli.tsне тронут (исправлен в #160).