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