opencode-config/tests/test_memory_doctor.ts
Sergey 7249f1fcb5
fix(memory): keyword search out-of-box + doctor gaps + tests (#124)
* fix(docker): add system ripgrep via apt for keyword search fallback

* fix(ci): install ripgrep and npm deps for keyword search in CI

* fix(memory-search): warn when ripgrep not resolvable instead of silent skip

* fix(memory-doctor): check rg status, arch mismatch, and require errors

* test(memory): add keyword search and doctor unit tests

* test(memory): unskip e2e keyword tests without RUN_LIVE

* docs(handoff): add handoff and ADR for keyword search fix

* test(memory): remove unused var in keyword search test

* docs(handoff): set PR number to 124

* docs(project-map): update after structural changes (PR#124)

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-29 17:20:59 +03:00

169 lines
No EOL
6.4 KiB
TypeScript

/**
* Tests for memory-doctor.ts diagnostic gaps.
*
* memory-doctor runs ripgrep --version and reports status. This file covers
* the four gaps fixed in PR for issue #123:
* G9 — allGreen must check rg --version status===0, not just rgBin!==null
* G2 — arch-mismatch detection when binary platform != runtime platform
* G3 — npm require-error surfaced in report when npm package missing
*
* Runtime note: opencode ships a standalone binary with Bun bundled inside;
* there is no separate `bun` CLI on the host (CI runner uses node + pytest).
* This file documents the intended TS-side test cases and is runnable under
* `bun test` once a bun runtime is available on the host.
*
* Test cases:
* - test_allGreen_false_on_broken_binary — rgPath exists but --version
* fails → report says NOT all green (G9)
* - test_arch_mismatch_detection — rgBin path contains 'darwin' but
* process.platform === 'linux' → report mentions arch mismatch (G2)
* - test_npm_missing_hint_in_report — rgBin===null, no system rg → report
* includes npm ci hint (G3)
* - test_allGreen_true_when_rg_works — rg --version succeeds → keyword
* works (sanity, G9 regression guard)
*/
import { describe, test, expect, mock } from "bun:test" with { type: "'bun-test'" }
import fs from "fs"
import os from "os"
import path from "path"
const DOCTOR_SRC = path.resolve(
import.meta.dir,
"..",
".opencode",
"tools",
"memory-doctor.ts"
)
function ctx(worktree = ".") {
return {
sessionID: "t",
messageID: "t",
agent: "t",
directory: ".",
worktree,
abort: new AbortController().signal,
metadata() {},
async ask() {},
}
}
describe("memory-doctor allGreen (G9)", () => {
test("test_allGreen_false_on_broken_binary — rgPath exists but --version fails → NOT all green", async () => {
const fakeRgPath = "/tmp/opencode/broken-rg"
mock.module("node:module", () => ({
createRequire: () => () => ({ rgPath: fakeRgPath }),
}))
mock.module("fs", () => ({
...fs,
existsSync: (p: string) =>
p === fakeRgPath || p === os.homedir() + "/.local/share/opencode/opencode-memory",
readdirSync: () => [],
statSync: () => ({ size: 0, mtime: new Date() }),
}))
mock.module("child_process", () => ({
spawnSync: (cmd: string, args: string[]) => {
if (cmd === fakeRgPath && args[0] === "--version") {
return { status: 1, stdout: "", stderr: "cannot execute binary" }
}
if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
if (cmd === "rg") return { status: 1, stdout: "", stderr: "" }
return { status: 0, stdout: "", stderr: "" }
},
}))
const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
const report = await mod.default.execute({}, ctx())
expect(report).toContain("ripgrep missing or broken")
})
test("test_allGreen_true_when_rg_works — rg --version succeeds → keyword works", async () => {
const fakeRgPath = "/tmp/opencode/good-rg"
mock.module("node:module", () => ({
createRequire: () => () => ({ rgPath: fakeRgPath }),
}))
mock.module("fs", () => ({
...fs,
existsSync: () => true,
readdirSync: () => [],
statSync: () => ({ size: 0, mtime: new Date() }),
}))
mock.module("child_process", () => ({
spawnSync: (cmd: string, args: string[]) => {
if (cmd === fakeRgPath && args[0] === "--version") {
return { status: 0, stdout: "ripgrep 13.0.0\n", stderr: "" }
}
if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
return { status: 0, stdout: "", stderr: "" }
},
}))
const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
const report = await mod.default.execute({}, ctx())
expect(report).toContain("All green")
})
})
describe("memory-doctor arch mismatch (G2)", () => {
test("test_arch_mismatch_detection — darwin binary on linux → report mentions arch mismatch", async () => {
// @vscode/ripgrep platform dir names include the OS: e.g.
// .../@vscode/ripgrep/bin/rg-darwin-arm64 or .../rg-linux-x64.
const fakeRgPath = "/tmp/opencode/@vscode/ripgrep/bin/rg-darwin-arm64"
const origPlatform = process.platform
Object.defineProperty(process, "platform", { value: "linux" })
try {
mock.module("node:module", () => ({
createRequire: () => () => ({ rgPath: fakeRgPath }),
}))
mock.module("fs", () => ({
...fs,
existsSync: () => true,
readdirSync: () => [],
statSync: () => ({ size: 0, mtime: new Date() }),
}))
mock.module("child_process", () => ({
spawnSync: (cmd: string, args: string[]) => {
if (cmd === fakeRgPath && args[0] === "--version") {
return { status: 1, stdout: "", stderr: "Exec format error" }
}
if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
return { status: 0, stdout: "", stderr: "" }
},
}))
const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
const report = await mod.default.execute({}, ctx())
expect(report).toContain("arch mismatch")
expect(report).toContain("darwin")
} finally {
Object.defineProperty(process, "platform", { value: origPlatform })
}
})
})
describe("memory-doctor npm missing hint (G3)", () => {
test("test_npm_missing_hint_in_report — rgBin===null, no system rg → report includes npm ci hint", async () => {
mock.module("node:module", () => ({
createRequire: () => () => {
throw new Error("Cannot find module '@vscode/ripgrep'")
},
}))
mock.module("fs", () => ({
...fs,
existsSync: (p: string) =>
p === os.homedir() + "/.local/share/opencode/opencode-memory",
readdirSync: () => [],
statSync: () => ({ size: 0, mtime: new Date() }),
}))
mock.module("child_process", () => ({
spawnSync: (cmd: string) => {
if (cmd === "rg") return { status: 127, stdout: "", stderr: "rg: not found" }
if (cmd === "python3") return { status: 0, stdout: "ok\n", stderr: "" }
return { status: 0, stdout: "", stderr: "" }
},
}))
const mod = await import(DOCTOR_SRC + "?t=" + Date.now())
const report = await mod.default.execute({}, ctx())
expect(report).toContain("npm package missing")
expect(report).toContain("npm ci")
expect(report).toContain("ripgrep missing or broken")
})
})