opencode-config/docs/handoff/pr-197-decouple-draw-image-tests-from-cover.md
Sergey 66f0afa58b
refactor(draw-image): decouple tests from production cover.svg (#197)
* refactor(draw-image): add tests/fixtures/cover.svg and loadFixture helper

* feat(draw-image): add --template-dir flag to cli for test isolation

* refactor(draw-image): switch tests from templates/cover to fixtures

* chore(draw-image): simplify production cover.svg now decoupled from tests

* docs(handoff): add handoff + ADR for issue #196

* docs(handoff): set PR number

* docs(project-map): update after structural changes

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-08-01 01:22:15 +03:00

27 lines
No EOL
6.2 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.

---
pr: 197
title: "refactor(draw-image): decouple tests from production cover.svg"
---
## Что сделано
Отвязал тесты движка draw-image от продакшн-шаблона `templates/cover.svg` (issue #196). Тесты теперь опираются на каноничный fixture, а продакшн-шаблон упрощён до чистой обложки — без badge/sub-icon/subtitle-слотов.
1. **`tests/fixtures/cover.svg`** — новый каноничный тестовый шаблон (1024×1024) со всеми типами слотов: `icon` (x=312,y=180,400×400,recolor=accent), `sub-icon` (x=412,y=600,200×200,recolor=accent), `badge` (x=780,y=780,180×180,bg=surface,border=accent,radius=0.5) и subtitle-строка `y="930"`. Стабильный контракт для тестов: badge x=780 y=780 w=180 h=180, subtitle y=930, icon recolor=accent.
2. **`tests/helpers/fixtures.ts`** — helper `loadFixture(name)`: читает из `tests/fixtures/`, не дублирует логику `loadTemplate`. Экспортирует `FIXTURES_DIR_ABS`.
3. **`--template-dir` флаг CLI** — `src/render.ts`: `loadTemplate(drawImageDir, name, templateDir?)` принимает опциональный `templateDir` (дефолт `"templates"`), поддерживает относительный (от `drawImageDir`) и абсолютный путь. `cli.ts`: парсит `--template-dir`, пробрасывает в `loadTemplate`. Без флага → `templates/` (обратная совместимость, продакшн не сломан). Usage-сообщение обновлено.
4. **9 тестов переведены на fixture**: `slot.test.ts`, `render.optional.test.ts`, `render.integration.test.ts`, `render.optional.integration.test.ts`, `e2e.test.ts`, `e2e.optional.test.ts`, `e2e.no-icon.test.ts`, `idempotency.test.ts`, `cleanup.test.ts`. В unit/integration — `loadTemplate(DRAW_IMAGE_DIR,"cover")``loadFixture("cover")`. В e2e (CLI) — добавлен `--template-dir tests/fixtures`. `e2e.bad-input.test.ts` не тронут (проверяет несуществующий шаблон, не зависит от cover.svg). В `idempotency.test.ts` убран неиспользуемый импорт `loadTemplate`/`loadBrand`/`buildSvg`/`computeHash`/`writeFileSync`.
5. **`templates/cover.svg` упрощён** — оставлен только icon-слот 520×520 (x=252,y=180) и title y=830. Удалены `sub-icon`, `badge`, subtitle-`<text>` — чистая обложка. Перерендерен `assets/cover.png` (icon=opencode, title="opencode config", без subtitle): PNG побайтово идентичен предыдущему (`cmp` → IDENTICAL, 18077 байт) — удалённые слоты в продакшн-рендере не использовались. Hash в `cover.meta.json` изменился (`d052d8a9...` vs `646197c4...`), т.к. `computeHash` хеширует весь templateSvg-контент, но пиксели идентичны.
6. Добавлен handoff + ADR-087.
## Почему
10 из 15 тестов жёстко привязывались к продакшн `templates/cover.svg` через `loadTemplate(DRAW_IMAGE_DIR,"cover")` и проверяли конкретные продакшн-значения (badge x=780 y=780, subtitle y=930). Любое изменение обложки (размер/позиция иконки, удаление subtitle) ломало тесты — PR#195 уже сталкивался с этим (пришлось сохранить subtitle-слот в шаблоне, чтобы не сломать `render.optional.test.ts`). Отвязка даёт свободу менять продакшн-обложку без правок тестов: тесты получили стабильный fixture-контракт, а продакшн-шаблон упрощён до того, что реально нужно для обложки. Упрощение cover.svg и зелёные тесты после — прямое подтверждение, что отвязка работает (критерий приёмки issue).
## Pending
— (после merge: fixture можно расширять новыми slot-типами без риска для продакшн-обложки)
## Watch out
- **`--template-dir` относительный путь** — `tests/fixtures` в e2e-тестах резолвится от `drawImageDir` (cwd CLI = `DRAW_IMAGE_DIR`), не от cwd запускающего. Абсолютный путь тоже поддерживается через `path.isAbsolute`. Продакшн-вызовы без флага используют `templates/` — обратно совместимо.
- **Hash `cover.meta.json` изменился** — `computeHash` (sha256) зависит от SVG-контента. Упрощённый шаблон короче → хеш другой (`d052d8a9...`), хотя PNG пиксели идентичны. Кэш-инвалидация ожидаема, на рендер не влияет.
- **ADR-087 / handoff файлы** — созданы через scaffold-handoff.sh, переименованы в `pr-197-*` / `087-pr-197-*` после create-pr (PR#197). Frontmatter `pr: 197`.
- **`e2e.bad-input.test.ts` не переведён** на `--template-dir` — он тестирует несуществующий шаблон `nonexistent`, который отсутствует в любом templateDir. Тест корректен для дефолтного `templates/` и не зависит от cover.svg — оставлен как есть (соответствует контракту issue: «оставь как есть»).
- **PNG-идентичность** — упрощение cover.svg не меняет пиксели, т.к. `render.ts` для пустых slots не вставляет SVG (нет `slotSvgs`), а conditional-regex ранее удалял subtitle-`<text>` при falsy `args.subtitle`. Удаление этих элементов из шаблона = удаление мёртвого кода для продакшн-рендера.