Files
continue/core/tools/parseArgs.vitest.ts
2025-12-02 20:28:45 +00:00

377 lines
12 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { ToolCallDelta } from "..";
import {
getBooleanArg,
getNumberArg,
getOptionalStringArg,
getStringArg,
safeParseToolCallArgs,
} from "./parseArgs";
describe("safeParseToolCallArgs", () => {
it("should parse valid JSON arguments", () => {
const toolCall: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: '{"name": "test", "value": 123}',
},
};
const result = safeParseToolCallArgs(toolCall);
expect(result).toEqual({ name: "test", value: 123 });
});
it("should parse valid JSON arguments with whitespace", () => {
const toolCall: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: ' {"name": "test", "value": 123} ',
},
};
const result = safeParseToolCallArgs(toolCall);
expect(result).toEqual({ name: "test", value: 123 });
});
it("should return empty object for invalid JSON", () => {
const toolCall: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: '{"name": "test", value: 123', // Invalid JSON
},
};
const result = safeParseToolCallArgs(toolCall);
expect(result).toEqual({});
});
it("should return empty object when arguments is empty", () => {
const toolCall: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: "",
},
};
const result = safeParseToolCallArgs(toolCall);
expect(result).toEqual({});
});
it("should return empty object when arguments is null or undefined", () => {
const toolCall1: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: null as any,
},
};
const toolCall2: ToolCallDelta = {
id: "1",
function: {
name: "testFunction",
arguments: undefined,
},
};
expect(safeParseToolCallArgs(toolCall1)).toEqual({});
expect(safeParseToolCallArgs(toolCall2)).toEqual({});
});
it("should handle case when function is undefined", () => {
const toolCall: ToolCallDelta = {
id: "1",
function: undefined,
};
const result = safeParseToolCallArgs(toolCall);
expect(result).toEqual({});
});
});
describe("getStringArg", () => {
it("should return string argument when valid", () => {
const args = { name: "test" };
const result = getStringArg(args, "name");
expect(result).toBe("test");
});
it("should throw error when argument is missing", () => {
const args = { otherArg: "test" };
expect(() => getStringArg(args, "name")).toThrowError(
"`name` argument is required and must not be empty or whitespace-only. (type string)",
);
});
it("should throw error when argument is not a string", () => {
const args = { name: 123 };
expect(() => getStringArg(args, "name")).toThrowError(
"`name` argument is required and must not be empty or whitespace-only. (type string)",
);
});
it("should throw error when argument is empty and empty not allowed", () => {
const args = { name: "" };
expect(() => getStringArg(args, "name")).toThrowError(
"Argument name must not be empty or whitespace-only",
);
const argsWithSpace = { name: " " };
expect(() => getStringArg(argsWithSpace, "name")).toThrowError(
"Argument name must not be empty or whitespace-only",
);
});
it("should accept empty string when allowEmpty is true", () => {
const args = { name: "" };
const result = getStringArg(args, "name", true);
expect(result).toBe("");
const argsWithSpace = { name: " " };
const resultWithSpace = getStringArg(argsWithSpace, "name", true);
expect(resultWithSpace).toBe(" ");
});
it("should handle null or undefined args", () => {
expect(() => getStringArg(null, "name")).toThrowError(
"`name` argument is required and must not be empty or whitespace-only. (type string)",
);
expect(() => getStringArg(undefined, "name")).toThrowError(
"`name` argument is required and must not be empty or whitespace-only. (type string)",
);
});
it("should convert parsed JSON object to string for contents parameter", () => {
// This simulates the case where JSON.parse() has converted a JSON string into an object
const args = { contents: { key: "value", number: 123 } };
const result = getStringArg(args, "contents");
expect(result).toBe('{"key":"value","number":123}');
});
it("should convert nested JSON object to string for contents parameter", () => {
const args = {
contents: {
user: {
name: "John",
details: {
age: 30,
preferences: ["coding", "reading"],
},
},
},
};
const result = getStringArg(args, "contents");
const expected =
'{"user":{"name":"John","details":{"age":30,"preferences":["coding","reading"]}}}';
expect(result).toBe(expected);
});
it("should convert JSON array to string for contents parameter", () => {
const args = { contents: ["item1", "item2", { key: "value" }] };
const result = getStringArg(args, "contents");
expect(result).toBe('["item1","item2",{"key":"value"}]');
});
it("should handle contents parameter that is already a string", () => {
const args = { contents: "already a string" };
const result = getStringArg(args, "contents");
expect(result).toBe("already a string");
});
it("should handle contents parameter that is null", () => {
const args = { contents: null };
expect(() => getStringArg(args, "contents")).toThrowError(
"`contents` argument is required and must not be empty or whitespace-only. (type string)",
);
});
});
describe("getOptionalStringArg", () => {
it("should return undefined when argument is not provided", () => {
const args = { otherArg: "test" };
const result = getOptionalStringArg(args, "name");
expect(result).toBeUndefined();
});
it("should return string argument when valid", () => {
const args = { name: "test" };
const result = getOptionalStringArg(args, "name");
expect(result).toBe("test");
});
it("should throw error when argument is not a string", () => {
const args = { name: 123 };
expect(() => getOptionalStringArg(args, "name")).toThrowError(
"`name` argument is required and must not be empty or whitespace-only. (type string)",
);
});
it("should throw error when argument is empty and empty not allowed", () => {
const args = { name: "" };
expect(() => getOptionalStringArg(args, "name")).toThrowError(
"Argument name must not be empty or whitespace-only",
);
});
it("should accept empty string when allowEmpty is true", () => {
const args = { name: "" };
const result = getOptionalStringArg(args, "name", true);
expect(result).toBe("");
});
it("should handle null or undefined args", () => {
expect(getOptionalStringArg(null, "name")).toBeUndefined();
expect(getOptionalStringArg(undefined, "name")).toBeUndefined();
});
});
describe("getBooleanArg", () => {
it("should return boolean argument when valid", () => {
const argsTrue = { flag: true };
const argsFalse = { flag: false };
expect(getBooleanArg(argsTrue, "flag")).toBe(true);
expect(getBooleanArg(argsFalse, "flag")).toBe(false);
});
it("should return undefined when argument is not provided and not required", () => {
const args = { otherArg: true };
const result = getBooleanArg(args, "flag");
expect(result).toBeUndefined();
});
it("should throw error when argument is not provided and required", () => {
const args = { otherArg: true };
expect(() => getBooleanArg(args, "flag", true)).toThrowError(
"Argument `flag` is required (type boolean)",
);
});
it('should handle string "true" and "false" values', () => {
const argsStringTrue = { flag: "true" };
const argsStringFalse = { flag: "false" };
const argsStringTrueUppercase = { flag: "TRUE" };
const argsStringFalseUppercase = { flag: "FALSE" };
expect(getBooleanArg(argsStringTrue, "flag")).toBe(true);
expect(getBooleanArg(argsStringFalse, "flag")).toBe(false);
expect(getBooleanArg(argsStringTrueUppercase, "flag")).toBe(true);
expect(getBooleanArg(argsStringFalseUppercase, "flag")).toBe(false);
});
it("should throw error when argument is not a boolean or valid boolean string", () => {
const argsNumber = { flag: 1 };
const argsInvalidString = { flag: "yes" };
const argsObject = { flag: {} };
expect(() => getBooleanArg(argsNumber, "flag")).toThrowError(
"Argument `flag` must be a boolean true or false",
);
expect(() => getBooleanArg(argsInvalidString, "flag")).toThrowError(
"Argument `flag` must be a boolean true or false",
);
expect(() => getBooleanArg(argsObject, "flag")).toThrowError(
"Argument `flag` must be a boolean true or false",
);
});
it("should handle null or undefined args", () => {
expect(getBooleanArg(null, "flag")).toBeUndefined();
expect(getBooleanArg(undefined, "flag")).toBeUndefined();
expect(() => getBooleanArg(null, "flag", true)).toThrowError(
"Argument `flag` is required (type boolean)",
);
expect(() => getBooleanArg(undefined, "flag", true)).toThrowError(
"Argument `flag` is required (type boolean)",
);
});
});
describe("getNumberArg", () => {
it("should return number argument when valid", () => {
const args = { count: 42 };
const result = getNumberArg(args, "count");
expect(result).toBe(42);
});
it("should parse string numbers", () => {
const args = { count: "42" };
const result = getNumberArg(args, "count");
expect(result).toBe(42);
});
it("should floor decimal numbers", () => {
const args = { count: 42.7 };
const result = getNumberArg(args, "count");
expect(result).toBe(42);
});
it("should parse and floor string decimal numbers", () => {
const args = { count: "42.7" };
const result = getNumberArg(args, "count");
expect(result).toBe(42);
});
it("should throw error when argument is missing", () => {
const args = { otherArg: 42 };
expect(() => getNumberArg(args, "count")).toThrowError(
"Argument `count` is required (type number)",
);
});
it("should throw error when argument is not a number", () => {
const argsString = { count: "not-a-number" };
const argsBoolean = { count: true };
const argsObject = { count: {} };
expect(() => getNumberArg(argsString, "count")).toThrowError(
"Argument `count` must be a valid number",
);
expect(() => getNumberArg(argsBoolean, "count")).toThrowError(
"Argument `count` must be a valid number",
);
expect(() => getNumberArg(argsObject, "count")).toThrowError(
"Argument `count` must be a valid number",
);
});
it("should throw error when argument is NaN", () => {
const args = { count: NaN };
expect(() => getNumberArg(args, "count")).toThrowError(
"Argument `count` must be a valid number",
);
});
it("should handle negative numbers", () => {
const args = { count: -5 };
const result = getNumberArg(args, "count");
expect(result).toBe(-5);
});
it("should handle negative string numbers", () => {
const args = { count: "-10" };
const result = getNumberArg(args, "count");
expect(result).toBe(-10);
});
it("should handle zero", () => {
const args = { count: 0 };
const result = getNumberArg(args, "count");
expect(result).toBe(0);
});
it("should handle null or undefined args", () => {
expect(() => getNumberArg(null, "count")).toThrowError(
"Argument `count` is required (type number)",
);
expect(() => getNumberArg(undefined, "count")).toThrowError(
"Argument `count` is required (type number)",
);
});
});