* 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>
58 lines
No EOL
3.3 KiB
Markdown
58 lines
No EOL
3.3 KiB
Markdown
---
|
||
pr: 102
|
||
title: E2E coverage for 4 hybrid-memory scenarios
|
||
---
|
||
|
||
## Что сделано
|
||
|
||
Создан `tests/test_memory_tools_e2e.py` с 4 E2E сценариями, покрывающими гибридную
|
||
память (keyword + semantic):
|
||
|
||
1. **zero-config keyword-only** — поиск работает без `OPENAI_BASE_URL` (semantic
|
||
возвращает `[]`), keyword через ripgrep находит файл.
|
||
2. **full hybrid** — semantic находит по точному термину и синониму (через
|
||
embedding), keyword находит по точному слову.
|
||
3. **fallback при падении OpenRouter** — при невалидном `OPENAI_API_KEY` semantic
|
||
возвращает `[]` (401 не retryable → embedder returns None), keyword работает.
|
||
4. **удаление файла** — после удаления .md + reindex, ни semantic, ни keyword не
|
||
находят удалённый файл.
|
||
|
||
Все тесты маркированы `@pytest.mark.skipif(not RUN_LIVE)` — запускаются только с
|
||
`RUN_LIVE=1`. Без env — 4 skipped.
|
||
|
||
Вспомогательные функции:
|
||
- `_rg_search(pattern, dir)` — keyword через ripgrep (subprocess), fallback на
|
||
`/root/.cache/opencode/bin/rg` если нет в PATH.
|
||
- `_semantic_search(query, index_dir, env)` — semantic через `python3 -m
|
||
src.memory search --json` (subprocess), с передачей кастомного env.
|
||
- `_reindex(memory_dir, index_dir)` — reindex + poll `reindex.log` до `done:`.
|
||
- `_make_memory_file(dir, term, synonym)` — tmp .md с frontmatter + уникальным
|
||
термином `zzuniqtestterm42` + опциональным синонимом.
|
||
|
||
## Почему
|
||
|
||
Фаза 1 (PR #100) сделала Python backend safe (lazy-init, return None). Фаза 2
|
||
(PR #101) создала 5 TS tools. Нужна E2E гарантия что keyword fallback работает
|
||
когда semantic недоступен — без E2E регрессии: lazy-init поломался → все tools
|
||
падают; fallback сломался → semantic-up → memory-down.
|
||
|
||
Подробное обоснование в `technical/memory-keyword-vs-semantic-decision.md` —
|
||
гибрид выбран вместо semantic-only именно из-за стабильности keyword path.
|
||
|
||
## Pending
|
||
|
||
—
|
||
|
||
## Watch out
|
||
|
||
- Тесты требуют `OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_EMBEDDING_MODEL` в env
|
||
(реальный OpenRouter, не mock). Запуск: `RUN_LIVE=1 uv run pytest
|
||
tests/test_memory_tools_e2e.py -v --no-cov`.
|
||
- Сценарий 4 (deletion) требует dummy-файл в memory_dir — иначе `run_index`
|
||
возвращает по `if not md_files` без обновления `index.json`, и удалённая запись
|
||
остаётся в индексе. Добавлен `_make_dummy_file`.
|
||
- `rg` может быть не в PATH — `_rg_bin()` пробует `shutil.which` затем
|
||
`/root/.cache/opencode/bin/rg`, pytest.skip если нет.
|
||
- Timeout: сценарий 3 — 300с (хотя 401 не retryable и embedder returns None
|
||
быстро, на всякий случай), сценарии 2/4 — 180с.
|
||
- Ruff: `subprocess.run` требует `check=False` (PLW1510) — добавлено явно. |