mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): reduce redundant slow tests
This commit is contained in:
parent
54d22b74b3
commit
b82a374c7d
8 changed files with 146 additions and 235 deletions
|
|
@ -1,23 +1,19 @@
|
|||
import { spawn } from "node:child_process";
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
realpathSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Args } from "../src/cli/args.ts";
|
||||
import { ENV_AGENT_DIR } from "../src/config.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createSessionManager } from "../src/main.ts";
|
||||
|
||||
const cliPath = resolve(__dirname, "../src/cli.ts");
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
@ -50,141 +46,128 @@ function hasSessionWithId(root: string, sessionId: string): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
interface CliDirs {
|
||||
agentDir: string;
|
||||
projectDir: string;
|
||||
sessionDir: string;
|
||||
}
|
||||
|
||||
async function runCli(
|
||||
args: string[] | ((dirs: CliDirs) => string[]),
|
||||
setup?: (dirs: CliDirs) => void,
|
||||
): Promise<{ code: number | null; agentDir: string; stderr: string }> {
|
||||
async function runCli(args: string[]): Promise<{ code: number | null; agentDir: string }> {
|
||||
const tempRoot = createTempDir();
|
||||
const dirs: CliDirs = {
|
||||
agentDir: join(tempRoot, "agent"),
|
||||
projectDir: join(tempRoot, "project"),
|
||||
sessionDir: join(tempRoot, "sessions"),
|
||||
};
|
||||
mkdirSync(dirs.agentDir, { recursive: true });
|
||||
mkdirSync(dirs.projectDir, { recursive: true });
|
||||
setup?.(dirs);
|
||||
const resolvedArgs = typeof args === "function" ? args(dirs) : args;
|
||||
const agentDir = join(tempRoot, "agent");
|
||||
const projectDir = join(tempRoot, "project");
|
||||
mkdirSync(agentDir, { recursive: true });
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
let stderr = "";
|
||||
const code = await new Promise<number | null>((resolvePromise, reject) => {
|
||||
const child = spawn(process.execPath, [cliPath, ...resolvedArgs], {
|
||||
cwd: dirs.projectDir,
|
||||
const child = spawn(process.execPath, [cliPath, ...args], {
|
||||
cwd: projectDir,
|
||||
env: {
|
||||
...process.env,
|
||||
[ENV_AGENT_DIR]: dirs.agentDir,
|
||||
[ENV_AGENT_DIR]: agentDir,
|
||||
PI_OFFLINE: "1",
|
||||
TSX_TSCONFIG_PATH: resolve(__dirname, "../../../tsconfig.json"),
|
||||
},
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr += chunk.toString();
|
||||
stdio: ["ignore", "ignore", "ignore"],
|
||||
});
|
||||
child.on("error", reject);
|
||||
child.on("close", resolvePromise);
|
||||
});
|
||||
|
||||
return { code, agentDir: dirs.agentDir, stderr };
|
||||
return { code, agentDir };
|
||||
}
|
||||
|
||||
function writeSession(sessionDir: string, cwd: string, id: string): void {
|
||||
writeFileSync(
|
||||
join(sessionDir, `${id}.jsonl`),
|
||||
`${JSON.stringify({ type: "session", version: 3, id, timestamp: new Date().toISOString(), cwd })}\n`,
|
||||
);
|
||||
function args(overrides: Partial<Args>): Args {
|
||||
return {
|
||||
messages: [],
|
||||
fileArgs: [],
|
||||
unknownFlags: new Map(),
|
||||
diagnostics: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("--session-id read-only commands", () => {
|
||||
it("does not reserve a session for --help", async () => {
|
||||
function persistSession(session: SessionManager, content: string): void {
|
||||
session.appendMessage({ role: "user", content, timestamp: Date.now() });
|
||||
session.appendMessage({
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "persisted" }],
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
model: "test",
|
||||
usage: {
|
||||
input: 1,
|
||||
output: 1,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: 2,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
describe("--session-id", () => {
|
||||
it("does not persist a custom ID for metadata commands", async () => {
|
||||
const result = await runCli(["--session-id", "read-only-help", "--help"]);
|
||||
|
||||
expect(result.code).toBe(0);
|
||||
expect(hasSessionWithId(join(result.agentDir, "sessions"), "read-only-help")).toBe(false);
|
||||
});
|
||||
|
||||
it("allows --no-session with --session-id", async () => {
|
||||
const result = await runCli(["--no-session", "--session-id", "ephemeral-id", "--help"]);
|
||||
it("creates missing IDs and reopens existing IDs in process", async () => {
|
||||
const tempRoot = createTempDir();
|
||||
const projectDir = join(tempRoot, "project");
|
||||
const sessionDir = join(tempRoot, "sessions");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
const settingsManager = SettingsManager.inMemory();
|
||||
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
expect(result.code).toBe(0);
|
||||
expect(hasSessionWithId(join(result.agentDir, "sessions"), "ephemeral-id")).toBe(false);
|
||||
});
|
||||
|
||||
it("does not reserve a session for --list-models", async () => {
|
||||
const result = await runCli(["--session-id", "read-only-models", "--list-models"]);
|
||||
|
||||
expect(result.code).toBe(0);
|
||||
expect(hasSessionWithId(join(result.agentDir, "sessions"), "read-only-models")).toBe(false);
|
||||
});
|
||||
|
||||
it("warns when a missing --session-id creates a new session", async () => {
|
||||
const result = await runCli((dirs) => [
|
||||
"--session-dir",
|
||||
dirs.sessionDir,
|
||||
"--session-id",
|
||||
"missing-session-id",
|
||||
"--model",
|
||||
"missing-model",
|
||||
"-p",
|
||||
"hi",
|
||||
]);
|
||||
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.stderr).toContain(
|
||||
"Warning: No project session found with id 'missing-session-id'; creating a new session with that id.",
|
||||
const readOnly = await createSessionManager(
|
||||
args({ sessionId: "read-only", help: true }),
|
||||
projectDir,
|
||||
sessionDir,
|
||||
settingsManager,
|
||||
);
|
||||
expect(readOnly.getSessionId()).toBe("read-only");
|
||||
expect(readOnly.getSessionFile()).toBeUndefined();
|
||||
|
||||
const created = await createSessionManager(
|
||||
args({ sessionId: "persisted-id" }),
|
||||
projectDir,
|
||||
sessionDir,
|
||||
settingsManager,
|
||||
);
|
||||
persistSession(created, "persist me");
|
||||
expect(consoleError).toHaveBeenCalledWith(expect.stringContaining("creating a new session"));
|
||||
|
||||
consoleError.mockClear();
|
||||
const reopened = await createSessionManager(
|
||||
args({ sessionId: "persisted-id" }),
|
||||
projectDir,
|
||||
sessionDir,
|
||||
settingsManager,
|
||||
);
|
||||
expect(reopened.getSessionFile()).toBe(created.getSessionFile());
|
||||
expect(consoleError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not warn when --session-id opens an existing session", async () => {
|
||||
const result = await runCli(
|
||||
(dirs) => [
|
||||
"--session-dir",
|
||||
dirs.sessionDir,
|
||||
"--session-id",
|
||||
"existing-session-id",
|
||||
"--model",
|
||||
"missing-model",
|
||||
"-p",
|
||||
"hi",
|
||||
],
|
||||
(dirs) => {
|
||||
mkdirSync(dirs.sessionDir, { recursive: true });
|
||||
writeSession(dirs.sessionDir, dirs.projectDir, "existing-session-id");
|
||||
},
|
||||
);
|
||||
it("rejects an existing fork target in process", async () => {
|
||||
const tempRoot = createTempDir();
|
||||
const projectDir = join(tempRoot, "project");
|
||||
const sessionDir = join(tempRoot, "sessions");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
const source = SessionManager.create(projectDir, sessionDir, { id: "source-id" });
|
||||
persistSession(source, "source");
|
||||
const target = SessionManager.create(projectDir, sessionDir, { id: "existing-id" });
|
||||
persistSession(target, "target");
|
||||
vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
vi.spyOn(process, "exit").mockImplementation((code) => {
|
||||
throw new Error(`exit:${code}`);
|
||||
});
|
||||
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.stderr).not.toContain("No project session found with id 'existing-session-id'");
|
||||
});
|
||||
|
||||
it("rejects an existing fork target session id", async () => {
|
||||
const result = await runCli(
|
||||
(dirs) => ["--session-dir", dirs.sessionDir, "--fork", "source-id", "--session-id", "existing-id", "-p", "hi"],
|
||||
(dirs) => {
|
||||
mkdirSync(dirs.sessionDir, { recursive: true });
|
||||
writeSession(dirs.sessionDir, dirs.projectDir, "source-id");
|
||||
writeSession(dirs.sessionDir, dirs.projectDir, "existing-id");
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.stderr).toContain("Session already exists with id 'existing-id'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("--session-id validation", () => {
|
||||
it("rejects ids invalid under SessionManager rules without stack traces", async () => {
|
||||
for (const id of ["-bad", "bad id"]) {
|
||||
const result = await runCli(["--session-id", id, "-p", "hi"]);
|
||||
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.stderr).toContain("Session id must be non-empty");
|
||||
expect(result.stderr).not.toContain("SessionManager.create");
|
||||
}
|
||||
await expect(
|
||||
createSessionManager(
|
||||
args({ fork: "source-id", sessionId: "existing-id" }),
|
||||
projectDir,
|
||||
sessionDir,
|
||||
SettingsManager.inMemory(),
|
||||
),
|
||||
).rejects.toThrow("exit:1");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue