Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/lib/clients/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function checkCommand(command: string, toolName: string): Promise<v
try {
await util.promisify(exec)(command);
}catch (error:any) {
if (error.stderr) {
if (error.code === 'ENOENT' || error.stderr) {
throw new MissingRequirementError(toolName);
}
}
Expand Down Expand Up @@ -50,24 +50,28 @@ export function openUrl(url: string): Promise<ChildProcess> {
}

export async function getVersion(toolName: string): Promise<string> {
let toolResponse: {stdout?: string; stderr?: string};

try {
const toolResponse = await util.promisify(exec)(`${toolName} --version`);
toolResponse = await util.promisify(exec)(`${toolName} --version`);
} catch (error) {
throw new Error(`Error getting ${toolName} version.`);
}

if (toolResponse.stderr) {
throw new Error(toolResponse.stderr);
}
if (toolResponse.stderr) {
throw new Error(`Error getting ${toolName} version.`);
}

try {
const versionMatch = toolResponse.stdout.match(/(\d+\.\d+\.\d+)/);
if (versionMatch) {
return versionMatch[1];
}
} catch (err) {
throw new Error(`Could not parse ${toolName} version.`);
}
} catch (error) {
if (toolResponse.stdout == null) {
throw new Error(`Error getting ${toolName} version.`);
}

return "";
const versionMatch = toolResponse.stdout.match(/(\d+\.\d+\.\d+)/);
if (versionMatch) {
return versionMatch[1];
}

throw new Error(
`Could not parse ${toolName} version from output: "${toolResponse.stdout}". Expected format: X.Y.Z`
);
}
29 changes: 26 additions & 3 deletions tests/libs/system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,15 @@ describe("System Functions - Error Paths", () => {
await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
});

test("getVersion returns '' if stdout is empty", async () => {
test("getVersion throws when stdout does not match version pattern", async () => {
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
stdout: "",
stderr: ""
}));
const result = await getVersion('git');
expect(result).toBe("");
const toolName = "git";
await expect(getVersion(toolName)).rejects.toThrow(
`Could not parse ${toolName} version from output`
);
});

test("getVersion throw error if stdout undefined", async () => {
Expand All @@ -87,6 +89,17 @@ describe("System Functions - Error Paths", () => {
await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
});

test("getVersion throws when stdout has non-matching version format (e.g. major-only)", async () => {
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
stdout: "Docker version 25",
stderr: ""
}));
const toolName = "docker";
await expect(getVersion(toolName)).rejects.toThrow(
`Could not parse ${toolName} version from output`
);
});

test("checkCommand returns false if the command does not exist", async () => {
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject({
stdout: "",
Expand All @@ -96,6 +109,16 @@ describe("System Functions - Error Paths", () => {
await expect(checkCommand(`${toolName} --version`, toolName)).rejects.toThrow(new MissingRequirementError(toolName));
});

test("checkCommand throws MissingRequirementError when binary is not installed (ENOENT)", async () => {
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject({
code: 'ENOENT',
stderr: '',
message: 'spawn ENOENT'
}));
const toolName = 'docker';
await expect(checkCommand(`${toolName} --version`, toolName)).rejects.toThrow(new MissingRequirementError(toolName));
});

test("executeCommand throws an error if the command fails", async () => {
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution failed")));
await expect(executeCommand({
Expand Down