forked from jason/echo
ver 0.9
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""test_features.py — end-to-end test of the v0.9 features against the mock OLRAPI.
|
||||
|
||||
Exercises the real shipped scripts (echo.py capture/resolve/recall/link + sweep.py)
|
||||
through subprocess, then reads ground truth back from the mock. No network, no creds,
|
||||
no live vault.
|
||||
|
||||
Run: python test_features.py [--port 8801]
|
||||
"""
|
||||
import argparse, json, os, subprocess, sys, time, urllib.request, urllib.error
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
SCRIPTS = HERE.parent / "echo-memory.plugin.src" / "skills" / "echo-memory" / "scripts"
|
||||
ECHO = SCRIPTS / "echo.py"
|
||||
SWEEP = SCRIPTS / "sweep.py"
|
||||
KEY = "241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab"
|
||||
|
||||
failures = []
|
||||
|
||||
|
||||
def check(name, cond, detail=""):
|
||||
print(f"{'ok ' if cond else 'FAIL'} {name}" + (f" -- {detail}" if not cond else ""))
|
||||
if not cond:
|
||||
failures.append(name)
|
||||
|
||||
|
||||
class Harness:
|
||||
def __init__(self, base):
|
||||
self.base = base
|
||||
|
||||
def http(self, method, url, body=None, headers=None):
|
||||
data = body.encode() if isinstance(body, str) else body
|
||||
req = urllib.request.Request(url, data=data, method=method,
|
||||
headers={"Authorization": f"Bearer {KEY}", **(headers or {})})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as r:
|
||||
return r.status, r.read().decode("utf-8", "replace")
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode("utf-8", "replace")
|
||||
except Exception as e:
|
||||
return 0, str(e)
|
||||
|
||||
def reset(self):
|
||||
self.http("POST", f"{self.base}/__debug__reset")
|
||||
|
||||
def seed(self, path, content):
|
||||
self.http("PUT", f"{self.base}/vault/{path}", content)
|
||||
|
||||
def ground(self, path):
|
||||
_, body = self.http("GET", f"{self.base}/__debug__?path={path}")
|
||||
return None if body == "<<MISSING>>" else body
|
||||
|
||||
def echo(self, *args):
|
||||
env = dict(os.environ, ECHO_BASE=self.base, ECHO_KEY=KEY, ECHO_VERIFY="1",
|
||||
ECHO_TODAY="2026-06-21")
|
||||
return subprocess.run([sys.executable, str(args[0]), *args[1:]],
|
||||
capture_output=True, text=True, env=env)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--port", type=int, default=8801)
|
||||
a = ap.parse_args()
|
||||
base = f"http://127.0.0.1:{a.port}"
|
||||
srv = subprocess.Popen([sys.executable, str(HERE / "mock_olrapi.py"), "--port", str(a.port)],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
||||
try:
|
||||
for _ in range(50):
|
||||
try:
|
||||
urllib.request.urlopen(f"{base}/__debug__reset", data=b"", timeout=1); break
|
||||
except Exception:
|
||||
time.sleep(0.1)
|
||||
h = Harness(base)
|
||||
h.reset()
|
||||
h.seed("_agent/echo-vault.md", "---\nschema_version: 2\n---\n# marker\n")
|
||||
|
||||
# 1. capture a person (with an alias)
|
||||
r = h.echo(ECHO, "capture", "Bob Smith", "--kind", "person", "--aliases", "bob,rs")
|
||||
note = h.ground("resources/people/bob-smith.md")
|
||||
check("capture person creates note", note is not None and "type: person" in note, r.stderr)
|
||||
check("capture stamps agent_written", note and "agent_written: true" in note)
|
||||
idx = h.ground("_agent/index/entities.json")
|
||||
check("index records entity", idx and "bob-smith" in idx and '"bob"' in idx)
|
||||
|
||||
# 2. resolve by alias
|
||||
r = h.echo(ECHO, "resolve", "bob")
|
||||
out = json.loads(r.stdout) if r.stdout.strip().startswith("{") else {}
|
||||
check("resolve alias -> path", out.get("match") and out.get("path") == "resources/people/bob-smith.md", r.stdout)
|
||||
|
||||
# 3. capture a company whose body mentions Bob Smith -> auto bidirectional link
|
||||
r = subprocess.run([sys.executable, str(ECHO), "capture", "MPM", "--kind", "company", "-"],
|
||||
input="Bob Smith is the principal here.\n", capture_output=True, text=True,
|
||||
env=dict(os.environ, ECHO_BASE=base, ECHO_KEY=KEY, ECHO_TODAY="2026-06-21"))
|
||||
mpm = h.ground("resources/companies/mpm.md")
|
||||
bob = h.ground("resources/people/bob-smith.md")
|
||||
check("auto-link forward (mpm -> bob)", mpm and "[[resources/people/bob-smith]]" in mpm, r.stdout + r.stderr)
|
||||
check("auto-link reciprocal (bob -> mpm)", bob and "[[resources/companies/mpm]]" in bob)
|
||||
|
||||
# 4. recall an entity surfaces its linked neighborhood (search is mocked empty,
|
||||
# so this exercises the index-resolve + 1-hop expansion path)
|
||||
r = h.echo(ECHO, "recall", "MPM")
|
||||
check("recall surfaces linked person", "resources/people/bob-smith.md" in r.stdout, r.stdout)
|
||||
|
||||
# 5. explicit link between two fresh notes
|
||||
h.echo(ECHO, "capture", "Alpha", "--kind", "concept")
|
||||
h.echo(ECHO, "capture", "Beta", "--kind", "concept")
|
||||
h.echo(ECHO, "link", "resources/concepts/alpha.md", "resources/concepts/beta.md")
|
||||
alpha = h.ground("resources/concepts/alpha.md")
|
||||
beta = h.ground("resources/concepts/beta.md")
|
||||
check("link adds A->B", alpha and "[[resources/concepts/beta]]" in alpha)
|
||||
check("link adds B->A", beta and "[[resources/concepts/alpha]]" in beta)
|
||||
|
||||
# 6. inbox capture (unknown home)
|
||||
h.echo(ECHO, "capture", "stray thought", "--inbox")
|
||||
inbox = h.ground("inbox/captures/inbox.md")
|
||||
check("inbox capture lands", inbox and "stray thought" in inbox)
|
||||
|
||||
# 7. sweep --apply rebuilds the index, symmetrizes, stamps schema 3
|
||||
r = h.echo(SWEEP, "--apply")
|
||||
marker = h.ground("_agent/echo-vault.md")
|
||||
check("sweep runs clean", r.returncode == 0, r.stdout + r.stderr)
|
||||
check("sweep stamps schema 3", marker and "schema_version=3" in marker.replace(": ", "="), marker)
|
||||
idx2 = h.ground("_agent/index/entities.json")
|
||||
check("sweep rebuilt index has all entities",
|
||||
idx2 and all(s in idx2 for s in ["bob-smith", "mpm", "alpha", "beta"]))
|
||||
|
||||
print(f"\n{len(failures)} failure(s)" if failures else "\nall feature tests passed")
|
||||
return 1 if failures else 0
|
||||
finally:
|
||||
srv.terminate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user