# 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`).