diff --git a/src/agents/skills-install-fallback.test.ts b/src/agents/skills-install-fallback.test.ts index c946c45f7c4..09daeea2ec3 100644 --- a/src/agents/skills-install-fallback.test.ts +++ b/src/agents/skills-install-fallback.test.ts @@ -70,15 +70,16 @@ async function writeSkillWithInstaller( return writeSkillWithInstallers(workspaceDir, name, [{ id: "deps", kind, ...extra }]); } -function mockBinaryAvailability(availableBinaries: string[]) { - const available = new Set(availableBinaries); +function mockAvailableBinaries(binaries: string[]) { + const available = new Set(binaries); hasBinaryMock.mockImplementation((bin: string) => available.has(bin)); } -function getAptGetCalls() { - return runCommandWithTimeoutMock.mock.calls.filter( +function assertNoAptGetFallbackCalls() { + const aptCalls = runCommandWithTimeoutMock.mock.calls.filter( (call) => Array.isArray(call[0]) && (call[0] as string[]).includes("apt-get"), ); + expect(aptCalls).toHaveLength(0); } describe("skills-install fallback edge cases", () => { @@ -109,41 +110,55 @@ describe("skills-install fallback edge cases", () => { await fs.rm(workspaceDir, { recursive: true, force: true }).catch(() => undefined); }); - it("apt-get available but sudo missing/unusable returns helpful error for go install", async () => { - // go not available, brew not available, apt-get + sudo are available, sudo check fails - mockBinaryAvailability(["apt-get", "sudo"]); + it("handles sudo probe failures for go install without apt fallback", async () => { + for (const testCase of [ + { + label: "sudo returns password required", + setup: () => + runCommandWithTimeoutMock.mockResolvedValueOnce({ + code: 1, + stdout: "", + stderr: "sudo: a password is required", + }), + assert: (result: { message: string; stderr: string }) => { + expect(result.message).toContain("sudo"); + expect(result.message).toContain("https://go.dev/doc/install"); + }, + }, + { + label: "sudo probe throws executable-not-found", + setup: () => + runCommandWithTimeoutMock.mockRejectedValueOnce( + new Error('Executable not found in $PATH: "sudo"'), + ), + assert: (result: { message: string; stderr: string }) => { + expect(result.message).toContain("sudo is not usable"); + expect(result.stderr).toContain("Executable not found"); + }, + }, + ]) { + runCommandWithTimeoutMock.mockClear(); + mockAvailableBinaries(["apt-get", "sudo"]); + testCase.setup(); - // sudo -n true fails (no passwordless sudo) - runCommandWithTimeoutMock.mockResolvedValueOnce({ - code: 1, - stdout: "", - stderr: "sudo: a password is required", - }); + const result = await installSkill({ + workspaceDir, + skillName: "go-tool-single", + installId: "deps", + }); - const result = await installSkill({ - workspaceDir, - skillName: "go-tool-single", - installId: "deps", - }); - - expect(result.ok).toBe(false); - expect(result.message).toContain("sudo"); - expect(result.message).toContain("https://go.dev/doc/install"); - - // Verify sudo -n true was called - expect(runCommandWithTimeoutMock).toHaveBeenCalledWith( - ["sudo", "-n", "true"], - expect.objectContaining({ timeoutMs: 5_000 }), - ); - - // Verify apt-get install was NOT called - const aptCalls = getAptGetCalls(); - expect(aptCalls).toHaveLength(0); + expect(result.ok, testCase.label).toBe(false); + testCase.assert(result); + expect(runCommandWithTimeoutMock, testCase.label).toHaveBeenCalledWith( + ["sudo", "-n", "true"], + expect.objectContaining({ timeoutMs: 5_000 }), + ); + assertNoAptGetFallbackCalls(); + } }); it("status-selected go installer fails gracefully when apt fallback needs sudo", async () => { - // no go/brew, but apt and sudo are present - mockBinaryAvailability(["apt-get", "sudo"]); + mockAvailableBinaries(["apt-get", "sudo"]); runCommandWithTimeoutMock.mockResolvedValueOnce({ code: 1, @@ -165,32 +180,8 @@ describe("skills-install fallback edge cases", () => { expect(result.message).toContain("sudo is not usable"); }); - it("handles sudo probe spawn failures without throwing", async () => { - // go not available, brew not available, apt-get + sudo appear available - mockBinaryAvailability(["apt-get", "sudo"]); - - runCommandWithTimeoutMock.mockRejectedValueOnce( - new Error('Executable not found in $PATH: "sudo"'), - ); - - const result = await installSkill({ - workspaceDir, - skillName: "go-tool-single", - installId: "deps", - }); - - expect(result.ok).toBe(false); - expect(result.message).toContain("sudo is not usable"); - expect(result.stderr).toContain("Executable not found"); - - // Verify apt-get install was NOT called - const aptCalls = getAptGetCalls(); - expect(aptCalls).toHaveLength(0); - }); - it("uv not installed and no brew returns helpful error without curl auto-install", async () => { - // uv not available, brew not available, curl IS available - mockBinaryAvailability(["curl"]); + mockAvailableBinaries(["curl"]); const result = await installSkill({ workspaceDir,