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
117 changes: 117 additions & 0 deletions packages/core/src/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,91 @@ describe("builtInValidationFunctions", () => {
});
});

describe("between", () => {
it("passes when number is within range", () => {
expect(builtInValidationFunctions.between(5, { min: 1, max: 10 })).toBe(
true,
);
expect(builtInValidationFunctions.between(1, { min: 1, max: 10 })).toBe(
true,
);
expect(builtInValidationFunctions.between(10, { min: 1, max: 10 })).toBe(
true,
);
});

it("fails when number is below range", () => {
expect(builtInValidationFunctions.between(0, { min: 1, max: 10 })).toBe(
false,
);
});

it("fails when number is above range", () => {
expect(builtInValidationFunctions.between(11, { min: 1, max: 10 })).toBe(
false,
);
});

it("fails for non-numbers", () => {
expect(builtInValidationFunctions.between("5", { min: 1, max: 10 })).toBe(
false,
);
});

it("fails when min or max is not provided", () => {
expect(builtInValidationFunctions.between(5, { min: 1 })).toBe(false);
expect(builtInValidationFunctions.between(5, { max: 10 })).toBe(false);
expect(builtInValidationFunctions.between(5, {})).toBe(false);
});
});

describe("lengthBetween", () => {
it("passes when string length is within range", () => {
expect(
builtInValidationFunctions.lengthBetween("hello", { min: 3, max: 10 }),
).toBe(true);
expect(
builtInValidationFunctions.lengthBetween("abc", { min: 3, max: 10 }),
).toBe(true);
expect(
builtInValidationFunctions.lengthBetween("abcdefghij", {
min: 3,
max: 10,
}),
).toBe(true);
});

it("fails when string is too short", () => {
expect(
builtInValidationFunctions.lengthBetween("ab", { min: 3, max: 10 }),
).toBe(false);
});

it("fails when string is too long", () => {
expect(
builtInValidationFunctions.lengthBetween("abcdefghijk", {
min: 3,
max: 10,
}),
).toBe(false);
});

it("fails for non-strings", () => {
expect(
builtInValidationFunctions.lengthBetween(12345, { min: 3, max: 10 }),
).toBe(false);
});

it("fails when min or max is not provided", () => {
expect(
builtInValidationFunctions.lengthBetween("hello", { min: 3 }),
).toBe(false);
expect(
builtInValidationFunctions.lengthBetween("hello", { max: 10 }),
).toBe(false);
});
});

describe("numeric", () => {
it("passes for numbers", () => {
expect(builtInValidationFunctions.numeric(42)).toBe(true);
Expand Down Expand Up @@ -602,6 +687,38 @@ describe("check helper", () => {
});
});

describe("between", () => {
it("creates between check with args", () => {
const c = check.between(1, 100, "Out of range");

expect(c.type).toBe("between");
expect(c.args).toEqual({ min: 1, max: 100 });
expect(c.message).toBe("Out of range");
});

it("uses default message", () => {
const c = check.between(0, 10);

expect(c.message).toBe("Must be between 0 and 10");
});
});

describe("lengthBetween", () => {
it("creates lengthBetween check with args", () => {
const c = check.lengthBetween(3, 50, "Invalid length");

expect(c.type).toBe("lengthBetween");
expect(c.args).toEqual({ min: 3, max: 50 });
expect(c.message).toBe("Invalid length");
});

it("uses default message", () => {
const c = check.lengthBetween(5, 100);

expect(c.message).toBe("Must be between 5 and 100 characters");
});
});

describe("url", () => {
it("creates url check", () => {
const c = check.url("Must be a URL");
Expand Down
40 changes: 40 additions & 0 deletions packages/core/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ export const builtInValidationFunctions: Record<string, ValidationFunction> = {
return value <= max;
},

/**
* Check if numeric value is within a range (inclusive).
* Combines min and max checks in a single validation.
*/
between: (value: unknown, args?: Record<string, unknown>) => {
if (typeof value !== "number") return false;
const min = args?.min;
const max = args?.max;
if (typeof min !== "number" || typeof max !== "number") return false;
return value >= min && value <= max;
},

/**
* Check if string length is within a range (inclusive).
* Combines minLength and maxLength checks in a single validation.
*/
lengthBetween: (value: unknown, args?: Record<string, unknown>) => {
if (typeof value !== "string") return false;
const min = args?.min;
const max = args?.max;
if (typeof min !== "number" || typeof max !== "number") return false;
return value.length >= min && value.length <= max;
},

/**
* Check if value is a number
*/
Expand Down Expand Up @@ -386,6 +410,22 @@ export const check = {
message: message ?? `Must be at most ${max}`,
}),

between: (min: number, max: number, message?: string): ValidationCheck => ({
type: "between",
args: { min, max },
message: message ?? `Must be between ${min} and ${max}`,
}),

lengthBetween: (
min: number,
max: number,
message?: string,
): ValidationCheck => ({
type: "lengthBetween",
args: { min, max },
message: message ?? `Must be between ${min} and ${max} characters`,
}),

url: (message = "Invalid URL"): ValidationCheck => ({
type: "url",
message,
Expand Down