mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor official hippo memory extension
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
async function executable(path: string, content: string): Promise<void> {
|
||||
await writeFile(path, `#!/bin/sh\n${content}\n`, "utf8");
|
||||
await chmod(path, 0o755);
|
||||
}
|
||||
|
||||
async function fixture(initialHippoVersion?: string) {
|
||||
const root = await mkdtemp(join(tmpdir(), "my-pi-hippo-scripts-"));
|
||||
const home = join(root, "home");
|
||||
const bin = join(root, "bin");
|
||||
const project = join(root, "project");
|
||||
const log = join(root, "commands.log");
|
||||
const versionState = join(root, "hippo-version");
|
||||
await mkdir(home, { recursive: true });
|
||||
await mkdir(bin, { recursive: true });
|
||||
await mkdir(project, { recursive: true });
|
||||
await writeFile(log, "", "utf8");
|
||||
if (initialHippoVersion) await writeFile(versionState, `${initialHippoVersion}\n`, "utf8");
|
||||
|
||||
await executable(join(bin, "pi"), 'printf "pi %s\\n" "$*" >> "$MOCK_LOG"');
|
||||
await executable(
|
||||
join(bin, "npm"),
|
||||
String.raw`printf 'npm %s\n' "$*" >> "$MOCK_LOG"
|
||||
if [ "$1" = "view" ]; then
|
||||
printf '%s\n' '1.33.0'
|
||||
elif [ "$1" = "install" ] && [ "$2" = "-g" ]; then
|
||||
printf '%s\n' '1.33.0' > "$HIPPO_VERSION_STATE"
|
||||
cat > "$MOCK_BIN/hippo" <<'HIPPO'
|
||||
#!/bin/sh
|
||||
if [ "$1" = "--version" ]; then cat "$HIPPO_VERSION_STATE"; exit 0; fi
|
||||
if [ "$1" = "init" ]; then mkdir -p .hippo; printf 'hippo init\n' >> "$MOCK_LOG"; exit 0; fi
|
||||
exit 0
|
||||
HIPPO
|
||||
chmod +x "$MOCK_BIN/hippo"
|
||||
fi`,
|
||||
);
|
||||
if (initialHippoVersion) {
|
||||
await executable(
|
||||
join(bin, "hippo"),
|
||||
String.raw`if [ "$1" = "--version" ]; then cat "$HIPPO_VERSION_STATE"; exit 0; fi
|
||||
if [ "$1" = "init" ]; then mkdir -p .hippo; printf 'hippo init\n' >> "$MOCK_LOG"; exit 0; fi
|
||||
exit 0`,
|
||||
);
|
||||
}
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
PATH: `${bin}:/usr/bin:/bin`,
|
||||
MOCK_BIN: bin,
|
||||
MOCK_LOG: log,
|
||||
HIPPO_VERSION_STATE: versionState,
|
||||
PI_PACKAGE_SOURCE: repositoryRoot,
|
||||
ZSH: "",
|
||||
ZSH_CUSTOM: "",
|
||||
ZDOTDIR: home,
|
||||
};
|
||||
return { root, home, bin, project, log, versionState, env };
|
||||
}
|
||||
|
||||
async function logText(path: string): Promise<string> {
|
||||
return readFile(path, "utf8");
|
||||
}
|
||||
|
||||
test("install.sh leaves Hippo absent when installation is declined", async () => {
|
||||
const f = await fixture();
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "install.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
input: "n\n".repeat(14),
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.doesNotMatch(await logText(f.log), /npm install -g hippo-memory/);
|
||||
assert.match(result.stdout, /已跳过 Hippo Memory CLI/);
|
||||
});
|
||||
|
||||
|
||||
test("install.sh installs the pinned Hippo CLI without choosing an init directory", async () => {
|
||||
const f = await fixture();
|
||||
const input = `y\n${"n\n".repeat(12)}`;
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "install.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
input,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm install -g hippo-memory@1\.33\.0/);
|
||||
assert.doesNotMatch(log, /hippo init/);
|
||||
assert.equal(await readFile(f.versionState, "utf8"), "1.33.0\n");
|
||||
assert.match(result.stdout, /未自动运行 hippo init/);
|
||||
});
|
||||
|
||||
test("update.sh leaves an already matching Hippo CLI untouched", async () => {
|
||||
const f = await fixture("1.33.0");
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm view hippo-memory@1\.33\.0 version/);
|
||||
assert.doesNotMatch(log, /npm install -g/);
|
||||
assert.match(result.stdout, /已是组合包目标版本/);
|
||||
});
|
||||
|
||||
test("update.sh upgrades an npm-managed older Hippo CLI exactly once", async () => {
|
||||
const f = await fixture("1.32.0");
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm list -g --depth=0 hippo-memory/);
|
||||
assert.equal((log.match(/npm install -g hippo-memory@1\.33\.0/g) ?? []).length, 1);
|
||||
assert.equal(await readFile(f.versionState, "utf8"), "1.33.0\n");
|
||||
});
|
||||
|
||||
test("update.sh preserves a Hippo executable that is not npm globally managed", async () => {
|
||||
const f = await fixture("1.32.0");
|
||||
await executable(
|
||||
join(f.bin, "npm"),
|
||||
String.raw`printf 'npm %s\n' "$*" >> "$MOCK_LOG"
|
||||
if [ "$1" = "view" ]; then printf '%s\n' '1.33.0'; exit 0; fi
|
||||
if [ "$1" = "list" ]; then exit 1; fi
|
||||
exit 0`,
|
||||
);
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 1);
|
||||
const log = await logText(f.log);
|
||||
assert.doesNotMatch(log, /npm install -g/);
|
||||
assert.match(result.stderr, /不属于 npm 全局/);
|
||||
});
|
||||
|
||||
|
||||
test("uninstall.sh keeps Hippo by default and removes only after explicit approval", async () => {
|
||||
const keep = await fixture("1.33.0");
|
||||
await mkdir(join(keep.project, ".hippo"), { recursive: true });
|
||||
await writeFile(join(keep.project, ".hippo", "sentinel"), "keep\n", "utf8");
|
||||
const keepResult = spawnSync("sh", [join(repositoryRoot, "uninstall.sh")], {
|
||||
cwd: keep.project,
|
||||
env: keep.env,
|
||||
input: "n\n",
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(keepResult.status, 0, keepResult.stderr || keepResult.stdout);
|
||||
assert.doesNotMatch(await logText(keep.log), /npm uninstall/);
|
||||
assert.equal(await readFile(join(keep.project, ".hippo", "sentinel"), "utf8"), "keep\n");
|
||||
|
||||
const remove = await fixture("1.33.0");
|
||||
await mkdir(join(remove.project, ".hippo"), { recursive: true });
|
||||
await writeFile(join(remove.project, ".hippo", "sentinel"), "keep\n", "utf8");
|
||||
const removeResult = spawnSync("sh", [join(repositoryRoot, "uninstall.sh")], {
|
||||
cwd: remove.project,
|
||||
env: remove.env,
|
||||
input: "y\n",
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(removeResult.status, 0, removeResult.stderr || removeResult.stdout);
|
||||
assert.match(await logText(remove.log), /npm uninstall -g hippo-memory/);
|
||||
assert.equal(await readFile(join(remove.project, ".hippo", "sentinel"), "utf8"), "keep\n");
|
||||
});
|
||||
Reference in New Issue
Block a user