mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 07:13:17 +08:00
- Rename platform/ to svc/ to avoid shadowing stdlib platform. - Replace from oclaw.* with from svc/runtime/interfaces; update -m CLI paths. - tests/conftest: prepend repo root to sys.path (no parent-folder package name). - CI: paths and offline_eval script under repo root. - Ops scripts: PYTHONPATH must be repo root for python -m runtime.* (fixes gateway/WhatsApp sidecar startup). - Fix default oclaw.json path in tabular/file attachment limits; stabilize attachment test config. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
from __future__ import annotations
|
||
|
||
import importlib.util
|
||
from pathlib import Path
|
||
import sys
|
||
from types import SimpleNamespace
|
||
|
||
from svc.config.paths import PROJECT_ROOT
|
||
|
||
|
||
def _wiki_search_handler(tmp_path: Path):
|
||
api_path = (PROJECT_ROOT / "runtime" / "extensions" / "memory-wiki" / "api.py").resolve()
|
||
spec = importlib.util.spec_from_file_location("memory_wiki_api_test", str(api_path))
|
||
assert spec is not None and spec.loader is not None
|
||
mod = importlib.util.module_from_spec(spec)
|
||
sys.modules[spec.name] = mod
|
||
spec.loader.exec_module(mod) # type: ignore[assignment]
|
||
wiki_root = tmp_path / "wiki"
|
||
wiki_root.mkdir(parents=True, exist_ok=True)
|
||
rows = mod.build_wiki_tool_specs(
|
||
SimpleNamespace(plugin_config={"wiki_root": str(wiki_root), "max_search_results": 10, "max_get_lines": 200})
|
||
)
|
||
for r in rows:
|
||
if str(r.get("name") or "") == "wiki_search":
|
||
return wiki_root, r["handler"]
|
||
raise AssertionError("wiki_search handler missing")
|
||
|
||
|
||
def test_wiki_search_returns_context_and_pagination_metadata(tmp_path: Path) -> None:
|
||
wiki_root, handler = _wiki_search_handler(tmp_path)
|
||
p = wiki_root / "users" / "identity.md"
|
||
p.parent.mkdir(parents=True, exist_ok=True)
|
||
p.write_text(
|
||
"\n".join(
|
||
[
|
||
"# Identity",
|
||
"creator is Oliver",
|
||
"project owner is Oliver",
|
||
"session profile line",
|
||
]
|
||
),
|
||
encoding="utf-8",
|
||
)
|
||
|
||
out = handler({"query": "Oliver", "limit": 1, "offset": 0, "context_lines": 1})
|
||
|
||
assert out.get("ok") is True
|
||
assert out.get("query") == "Oliver"
|
||
assert out.get("truncated") is True
|
||
assert out.get("next_offset") == 1
|
||
hits = out.get("hits") or []
|
||
assert len(hits) == 1
|
||
hit = hits[0]
|
||
assert hit.get("path") == "users/identity.md"
|
||
assert isinstance(hit.get("before"), list)
|
||
assert isinstance(hit.get("after"), list)
|
||
assert "matched_query" in hit
|
||
|
||
|
||
def test_wiki_search_offset_paginates_deterministically(tmp_path: Path) -> None:
|
||
wiki_root, handler = _wiki_search_handler(tmp_path)
|
||
p = wiki_root / "identity.md"
|
||
p.write_text("Oliver one\nOliver two\nOliver three\n", encoding="utf-8")
|
||
|
||
page1 = handler({"query": "Oliver", "limit": 2, "offset": 0})
|
||
page2 = handler({"query": "Oliver", "limit": 2, "offset": 2})
|
||
|
||
hits1 = page1.get("hits") or []
|
||
hits2 = page2.get("hits") or []
|
||
assert [h.get("line") for h in hits1] == [1, 2]
|
||
assert [h.get("line") for h in hits2] == [3]
|
||
assert page2.get("truncated") is False
|
||
assert page2.get("next_offset") is None
|
||
|
||
|
||
def test_wiki_search_expand_query_reports_attempted_queries(tmp_path: Path) -> None:
|
||
wiki_root, handler = _wiki_search_handler(tmp_path)
|
||
p = wiki_root / "users" / "identity.md"
|
||
p.parent.mkdir(parents=True, exist_ok=True)
|
||
p.write_text("项目所有者:Oliver\n", encoding="utf-8")
|
||
|
||
out = handler({"query": "用户", "expand_query": True, "max_rounds": 3, "limit": 5})
|
||
assert out.get("ok") is True
|
||
attempted = out.get("queries_attempted") or []
|
||
assert attempted
|
||
assert attempted[0] == "用户"
|
||
assert len(attempted) <= 3
|
||
hits = out.get("hits") or []
|
||
assert hits
|
||
assert any(h.get("matched_query") != "用户" for h in hits)
|
||
|
||
|
||
def test_wiki_search_keeps_backward_compatible_keys(tmp_path: Path) -> None:
|
||
wiki_root, handler = _wiki_search_handler(tmp_path)
|
||
(wiki_root / "a.md").write_text("hello\n", encoding="utf-8")
|
||
out = handler({"query": "hello"})
|
||
assert {"ok", "query", "hits", "truncated"}.issubset(set(out.keys()))
|
||
|