test(pi-claude-cli): cover async CLI validation probes
Adds vitest coverage for validateCliPresenceAsync and validateCliAuthAsync,
verifying success paths, spawn-error paths, non-zero exit codes, and that
auth failures emit the expected warning. Pairs with the async/memoized
validation refactor in f7df0d4e3 that unblocks the dashboard event loop
on every chat send.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,8 @@ import {
|
||||
captureStderr,
|
||||
validateCliPresence,
|
||||
validateCliAuth,
|
||||
validateCliPresenceAsync,
|
||||
validateCliAuthAsync,
|
||||
forceKillProcess,
|
||||
registerProcess,
|
||||
killAllProcesses,
|
||||
@@ -407,6 +409,94 @@ describe("validateCliAuth", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateCliPresenceAsync", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("resolves ok=true when claude --version exits 0", async () => {
|
||||
const EventEmitter = require("node:events");
|
||||
(spawn as any).mockImplementationOnce(() => {
|
||||
const proc = new EventEmitter();
|
||||
proc.kill = vi.fn();
|
||||
setImmediate(() => proc.emit("exit", 0));
|
||||
return proc;
|
||||
});
|
||||
|
||||
const result = await validateCliPresenceAsync();
|
||||
expect(result).toEqual({ ok: true });
|
||||
const args = (spawn as any).mock.calls[0][1] as string[];
|
||||
expect(args).toEqual(["--version"]);
|
||||
});
|
||||
|
||||
it("resolves ok=false with install message when spawn errors", async () => {
|
||||
const EventEmitter = require("node:events");
|
||||
(spawn as any).mockImplementationOnce(() => {
|
||||
const proc = new EventEmitter();
|
||||
proc.kill = vi.fn();
|
||||
setImmediate(() => proc.emit("error", new Error("ENOENT")));
|
||||
return proc;
|
||||
});
|
||||
|
||||
const result = await validateCliPresenceAsync();
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error.message).toContain("Claude Code CLI not found");
|
||||
expect(result.error.message).toContain("npm install");
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves ok=false when claude --version exits non-zero", async () => {
|
||||
const EventEmitter = require("node:events");
|
||||
(spawn as any).mockImplementationOnce(() => {
|
||||
const proc = new EventEmitter();
|
||||
proc.kill = vi.fn();
|
||||
setImmediate(() => proc.emit("exit", 1));
|
||||
return proc;
|
||||
});
|
||||
|
||||
const result = await validateCliPresenceAsync();
|
||||
expect(result.ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateCliAuthAsync", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("resolves true when claude auth status exits 0", async () => {
|
||||
const EventEmitter = require("node:events");
|
||||
(spawn as any).mockImplementationOnce(() => {
|
||||
const proc = new EventEmitter();
|
||||
proc.kill = vi.fn();
|
||||
setImmediate(() => proc.emit("exit", 0));
|
||||
return proc;
|
||||
});
|
||||
|
||||
expect(await validateCliAuthAsync()).toBe(true);
|
||||
const args = (spawn as any).mock.calls[0][1] as string[];
|
||||
expect(args).toEqual(["auth", "status"]);
|
||||
});
|
||||
|
||||
it("resolves false and warns when claude auth status fails", async () => {
|
||||
const EventEmitter = require("node:events");
|
||||
(spawn as any).mockImplementationOnce(() => {
|
||||
const proc = new EventEmitter();
|
||||
proc.kill = vi.fn();
|
||||
setImmediate(() => proc.emit("exit", 1));
|
||||
return proc;
|
||||
});
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
expect(await validateCliAuthAsync()).toBe(false);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("not authenticated"),
|
||||
);
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("CLI flags", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
Reference in New Issue
Block a user