Files
my-pi/pi-hermes-memory/tests/store/sqlite-native.test.ts
T
云服务部-叶林立 312bb2ba5a feat(pi-hermes-memory): 导入上游 v0.9.10 源码快照
从官方 Git 仓库导入 chandra447/pi-hermes-memory 在 tag v0.9.10
(df624056a2e8c68bc89ba0a81e1c120856ec9665,2026-10-02)的源码快照。
本次仅导入源码,尚未接入组合包:package.json、加载顺序、依赖与权限策略
均未改动。

- 原样导入 src/、tests/、docs/、scripts/、README、CHANGELOG、PLAN、
  AGENTS 与 LICENSE;该快照共 156 个上游文件。
- 剔除嵌套 .git、上游 .github 工作流、.ralph/、.claude/、node_modules、
  覆盖率与构建产物。
- 新增中文 UPSTREAM.md,记录来源,以及启用前必须完成的待决事项:
  在 pi-compaction 之前注册加载顺序、better-sqlite3 版本与根 allowScripts
  对齐、strip-ansi 解析确认。
- 说明选择 tag 而非 main 的原因:导入时上游 main 已领先至
  24fcd8ad26fc27badb8d7dbaf8034645de07b893(2026-10-11),选 tag 是为
  对应一个已发布、可复现的版本。

尚未接线。启用前还需反转 tests/extension-load.test.ts 中"不得加载
Hermes"的断言,并同步 pi-tool-search 分组、权限策略与文档。

验证:快照 156 个上游文件全部导入,目录内仅多出 UPSTREAM.md 一份;
已确认与上游 main 内容存在差异,证明取的是 tag 而非 main。
2026-10-13 23:19:35 -07:00

165 lines
5.0 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import path from "node:path";
import {
BetterSqlite3LoadError,
formatBetterSqlite3AbiError,
isNativeModuleAbiMismatch,
loadBetterSqlite3,
resolveBetterSqlite3PackageRoot,
} from "../../src/store/sqlite-native.js";
const require = createRequire(import.meta.url);
type RequireCache = Record<string, unknown>;
function fakeRequire(
impl: (id: string) => unknown,
resolveImpl: (id: string) => string,
): NodeRequire {
return Object.assign(impl, {
resolve: resolveImpl,
cache: {} as RequireCache,
}) as NodeRequire;
}
describe("sqlite-native loader", () => {
it("detects NODE_MODULE_VERSION ABI mismatch errors", () => {
assert.equal(
isNativeModuleAbiMismatch(
new Error(
"The module '/tmp/better_sqlite3.node' was compiled against a different Node.js version using NODE_MODULE_VERSION 115",
),
),
true,
);
assert.equal(isNativeModuleAbiMismatch(new Error("SQLITE_BUSY")), false);
});
it("detects ERR_DLOPEN_FAILED codes", () => {
const err = Object.assign(new Error("dlopen failed"), { code: "ERR_DLOPEN_FAILED" });
assert.equal(isNativeModuleAbiMismatch(err), true);
});
it("resolves the installed better-sqlite3 package root", () => {
const root = resolveBetterSqlite3PackageRoot(require);
assert.ok(root);
assert.equal(path.basename(root!), "better-sqlite3");
});
it("loads better-sqlite3 successfully in the current runtime", () => {
const Database = loadBetterSqlite3({ requireImpl: require, allowRebuild: false });
const db = new (Database as new (path: string) => {
exec: (sql: string) => void;
close: () => void;
})(":memory:");
db.exec("SELECT 1");
db.close();
});
it("rebuilds once on ABI mismatch then succeeds", () => {
let calls = 0;
const ctor = class FakeDb {
constructor(_path: string) {}
};
const req = fakeRequire(
(id: string) => {
assert.equal(id, "better-sqlite3");
calls += 1;
if (calls === 1) {
throw new Error(
"was compiled against a different Node.js version using NODE_MODULE_VERSION 115. Please try re-compiling",
);
}
return ctor;
},
(id: string) => {
assert.equal(id, "better-sqlite3");
return path.join("/tmp/fake-npm/node_modules/better-sqlite3/lib/index.js");
},
);
let rebuilt = false;
const Database = loadBetterSqlite3({
requireImpl: req,
rebuild: (packageRoot) => {
rebuilt = true;
assert.equal(packageRoot, "/tmp/fake-npm/node_modules/better-sqlite3");
return { ok: true, detail: "rebuilt" };
},
});
assert.equal(Database, ctor);
assert.equal(rebuilt, true);
assert.equal(calls, 2);
});
it("throws BetterSqlite3LoadError with recovery guidance when rebuild fails", () => {
const req = fakeRequire(
(_id: string) => {
throw new Error(
"The module 'x.node' was compiled against a different Node.js version using NODE_MODULE_VERSION 108",
);
},
() => path.join("/tmp/fake-npm/node_modules/better-sqlite3/lib/index.js"),
);
assert.throws(
() =>
loadBetterSqlite3({
requireImpl: req,
rebuild: () => ({ ok: false, detail: "npm missing" }),
}),
(error: unknown) => {
assert.ok(error instanceof BetterSqlite3LoadError);
const message = error.message;
assert.match(message, /pi-hermes-memory could not load the native better-sqlite3 module/);
assert.match(message, /npm rebuild better-sqlite3/);
assert.match(message, /Homebrew/);
assert.match(message, /npm missing/);
assert.equal(error.packageRoot, "/tmp/fake-npm/node_modules/better-sqlite3");
return true;
},
);
});
it("formats actionable ABI recovery text", () => {
const message = formatBetterSqlite3AbiError({
originalError: new Error("NODE_MODULE_VERSION 115"),
packageRoot: "/Users/me/.pi/agent/npm/node_modules/better-sqlite3",
rebuildAttempted: true,
rebuildDetail: "exit 1",
});
assert.match(message, /NODE_MODULE_VERSION/);
assert.match(message, /\/Users\/me\/\.pi\/agent\/npm\/node_modules\/better-sqlite3/);
assert.match(message, /npm rebuild better-sqlite3/);
assert.match(message, /Homebrew/);
});
it("does not rebuild for non-ABI load failures", () => {
let rebuilt = false;
const req = fakeRequire(
() => {
throw new Error("Cannot find module 'better-sqlite3'");
},
() => {
throw new Error("not found");
},
);
assert.throws(
() =>
loadBetterSqlite3({
requireImpl: req,
rebuild: () => {
rebuilt = true;
return { ok: true, detail: "should not run" };
},
}),
/Cannot find module 'better-sqlite3'/,
);
assert.equal(rebuilt, false);
});
});