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

6.2 KiB
Raw Permalink Blame History

pr title
197 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 флаг CLIsrc/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. Удаление этих элементов из шаблона = удаление мёртвого кода для продакшн-рендера.