opencode-config/docs/decisions/044-pr-102-e2e-memory-tests.md
Sergey 9d1373a51b
test(memory): E2E coverage for 4 scenarios (#102)
* test(memory): E2E coverage for 4 scenarios

* docs(handoff): set PR number

* style(memory): ruff format e2e test

* docs(project-map): add test_memory_tools_e2e.py after PR#102

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-27 01:32:53 +03:00

45 lines
No EOL
2.9 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.

# ADR-044: E2E test structure for hybrid memory (subprocess-based)
## Статус
Accepted (2026-07-26)
## Контекст
Гибридная память (keyword ripgrep + semantic Python OpenRouter) после рефактора
(PR #100 lazy-init, PR #101 5 TS tools) нуждалась в E2E гарантии стабильности.
Существующие unit-тесты (`test_search.py`, `test_index.py`, `test_embedder.py`)
мокают `embed_texts` — не проверяют реальный keyword path (ripgrep subprocess) и
не проверяют fallback при реальном падении OpenRouter.
Нужно было решить: как структурировать E2E тесты — через TS tools (spawnSync TS
plugin) или напрямую через Python `src.memory` + ripgrep subprocess.
## Решение
Тесты вызывают Python `src.memory` (search/index) и ripgrep напрямую через
`subprocess.run` из Python тестов, НЕ через TS tools. Причины:
1. **Изоляция** — тесты проверяют Python backend + ripgrep отдельно от TS
обёртки. TS tool — тонкий слой spawnSync, его логика (scoring, merge) уже
покрыта unit-тестами плагина.
2. **Простота env** — Python subprocess принимает `env` dict напрямую, можно
удалить `OPENAI_BASE_URL` (сценарий 1) или подменить `OPENAI_API_KEY` (сценарий
3) без манипуляций с process.env в TS.
3. **Скорость** — 4 теста прошли за 34с с реальным OpenRouter. TS tool добавил
бы накладные расходы на import/compile.
4. **Переиспользование паттерна**`test_embedder_live.py` уже использует
`@pytest.mark.skipif(not RUN_LIVE)` — тот же маркер на module level.
Каждый сценарий создаёт tmp memoryDir через `tmp_path` fixture, пишет .md с
уникальным термином `zzuniqtestterm42`, вызывает `_reindex` (poll `reindex.log`)
и `_semantic_search`/`_rg_search`, сравнивает результаты.
## Альтернативы
- **TS tool через subprocess** (`npx opencode-tool memory-search`) — отклонено:
сложно передать кастомный env, медленнее, тестирует TS обёртку а не backend.
- **pytest + import src.memory in-process** — отклонено: `embed_texts` читает
`os.environ` при вызове (lazy после PR #100), но monkeypatch на module-level
ломает параллельные тесты. Subprocess даёт полную изоляцию env per-test.
- **Mock OpenRouter через httpx mock** — отклонено: цель E2E = реальный API,
мок = unit-тест (уже есть `test_embedder.py`).