Files
my-pi/tests/hippo-extension.test.ts
T

68 lines
2.9 KiB
TypeScript

import assert from "node:assert/strict";
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import hippoExtension from "../hippo-memory-pi/index.ts";
test("official Hippo extension registers five tools and runs lifecycle CLI calls", async () => {
const root = await mkdtemp(join(tmpdir(), "my-pi-hippo-extension-"));
const bin = join(root, "bin");
const project = join(root, "project");
const log = join(root, "hippo.log");
await mkdir(bin, { recursive: true });
await mkdir(join(project, ".hippo"), { recursive: true });
const hippo = join(bin, "hippo");
await writeFile(
hippo,
`#!/bin/sh\nprintf '%s\\n' "$*" >> "$HIPPO_TEST_LOG"\ncase "$1" in\n status) printf '%s\\n' 'Hippo status ok' ;;\n context) printf '%s\\n' 'Remembered project context for testing' ;;\n recall) printf '%s\\n' 'Relevant project memory' ;;\n remember) printf '%s\\n' 'Remembered [test]' ;;\n outcome) printf '%s\\n' 'Outcome recorded' ;;\n sleep) printf '%s\\n' 'Sleep complete' ;;\nesac\n`,
"utf8",
);
await chmod(hippo, 0o755);
const previousPath = process.env.PATH;
const previousLog = process.env.HIPPO_TEST_LOG;
process.env.PATH = `${bin}:${previousPath ?? ""}`;
process.env.HIPPO_TEST_LOG = log;
try {
const handlers = new Map<string, Array<(event: any, ctx: any) => Promise<any>>>();
const tools = new Map<string, any>();
hippoExtension({
on(name: string, handler: (event: any, ctx: any) => Promise<any>) {
const entries = handlers.get(name) ?? [];
entries.push(handler);
handlers.set(name, entries);
},
registerTool(tool: any) {
tools.set(tool.name, tool);
},
});
assert.deepEqual(
[...tools.keys()].sort(),
["hippo_context", "hippo_outcome", "hippo_recall", "hippo_remember", "hippo_status"],
);
const ctx = { cwd: project };
const startResult = await handlers.get("session_start")?.[0]?.({}, ctx);
assert.match(startResult.systemPromptAppend, /Remembered project context/);
const rememberResult = await tools.get("hippo_remember").execute("call-1", { text: "A durable lesson" }, undefined, undefined, ctx);
assert.match(rememberResult.content[0].text, /Remembered \[test\]/);
await handlers.get("tool_result")?.[0]?.(
{ isError: true, toolName: "build", content: "Compilation failed because the generated symbol was missing" },
ctx,
);
await handlers.get("session_shutdown")?.[0]?.({}, ctx);
const calls = await readFile(log, "utf8");
assert.match(calls, /context --auto/);
assert.match(calls, /remember A durable lesson/);
assert.match(calls, /remember Tool 'build' failed:/);
assert.match(calls, /sleep/);
} finally {
process.env.PATH = previousPath;
if (previousLog === undefined) delete process.env.HIPPO_TEST_LOG;
else process.env.HIPPO_TEST_LOG = previousLog;
}
});