Skip to content

Commit e920306

Browse files
committed
feat: add validation utilities for common patterns
1 parent ce86f00 commit e920306

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

src/utils/validation.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @fileoverview Unit tests for validation utilities and schemas
3+
* @module utils/validation.test
4+
*/
5+
6+
import { describe, expect, it } from 'vitest';
7+
import { dateSchema, timestampSchema, urlSchema, validateInput } from './validation.js';
8+
9+
/**
10+
* Test suite for validation schemas
11+
*/
12+
describe('validation schemas', () => {
13+
/**
14+
* Tests for URL validation schema
15+
*/
16+
describe('urlSchema', () => {
17+
it('should accept valid URLs', () => {
18+
expect(urlSchema.parse('https://example.com')).toBe('https://example.com');
19+
expect(urlSchema.parse('http://example.com/path')).toBe('http://example.com/path');
20+
});
21+
22+
it('should reject invalid URLs', () => {
23+
expect(() => urlSchema.parse('not a url')).toThrow();
24+
expect(() => urlSchema.parse('example.com')).toThrow();
25+
});
26+
});
27+
28+
/**
29+
* Tests for date validation schema (YYYY-MM-DD format)
30+
*/
31+
describe('dateSchema', () => {
32+
it('should accept valid dates', () => {
33+
expect(dateSchema.parse('2024-01-01')).toBe('2024-01-01');
34+
expect(dateSchema.parse('2024-12-31')).toBe('2024-12-31');
35+
});
36+
37+
it('should reject invalid dates', () => {
38+
expect(() => dateSchema.parse('2024-1-1')).toThrow();
39+
expect(() => dateSchema.parse('01-01-2024')).toThrow();
40+
expect(() => dateSchema.parse('2024/01/01')).toThrow();
41+
});
42+
});
43+
44+
/**
45+
* Tests for timestamp validation schema (YYYYMMDDHHmmss format)
46+
*/
47+
describe('timestampSchema', () => {
48+
it('should accept valid timestamps', () => {
49+
expect(timestampSchema.parse('20240101120000')).toBe('20240101120000');
50+
});
51+
52+
it('should reject invalid timestamps', () => {
53+
expect(() => timestampSchema.parse('2024-01-01')).toThrow();
54+
expect(() => timestampSchema.parse('202401011200')).toThrow();
55+
});
56+
});
57+
});
58+
59+
/**
60+
* Test suite for the validateInput utility function
61+
*/
62+
describe('validateInput', () => {
63+
it('should return parsed value for valid input', () => {
64+
const result = validateInput(urlSchema, 'https://example.com');
65+
expect(result).toBe('https://example.com');
66+
});
67+
68+
it('should throw formatted error for invalid input', () => {
69+
expect(() => validateInput(urlSchema, 'invalid')).toThrow('Validation failed');
70+
});
71+
72+
it('should re-throw non-ZodError errors', () => {
73+
// Create a schema that throws a non-ZodError
74+
const faultySchema = {
75+
parse: () => {
76+
throw new Error('Not a ZodError');
77+
},
78+
// biome-ignore lint/suspicious/noExplicitAny: Testing error handling
79+
} as any;
80+
81+
expect(() => validateInput(faultySchema, 'input')).toThrow('Not a ZodError');
82+
});
83+
});

src/utils/validation.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @fileoverview Common validation schemas and utilities for input validation
3+
* @module utils/validation
4+
*/
5+
6+
import { z } from 'zod';
7+
8+
/**
9+
* Common validation schemas for reuse across tools
10+
*/
11+
12+
/**
13+
* Schema for validating URL strings
14+
* @description Ensures the input is a valid URL format
15+
* @example
16+
* ```typescript
17+
* const validUrl = urlSchema.parse("https://example.com");
18+
* // Returns: "https://example.com"
19+
* ```
20+
*/
21+
export const urlSchema = z.string().url('Invalid URL format');
22+
23+
/**
24+
* Schema for validating date strings in YYYY-MM-DD format
25+
* @description Validates that the input matches the YYYY-MM-DD date format
26+
* @example
27+
* ```typescript
28+
* const validDate = dateSchema.parse("2024-01-15");
29+
* // Returns: "2024-01-15"
30+
* ```
31+
*/
32+
export const dateSchema = z
33+
.string()
34+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in YYYY-MM-DD format');
35+
36+
/**
37+
* Schema for validating timestamp strings in YYYYMMDDHHmmss format
38+
* @description Validates 14-digit timestamp strings commonly used by web archives
39+
* @example
40+
* ```typescript
41+
* const validTimestamp = timestampSchema.parse("20240115143022");
42+
* // Returns: "20240115143022" (2024-01-15 14:30:22)
43+
* ```
44+
*/
45+
export const timestampSchema = z
46+
.string()
47+
.regex(/^\d{14}$/, 'Timestamp must be in YYYYMMDDHHmmss format');
48+
49+
/**
50+
* Validate and parse input with helpful error messages
51+
* @param schema - Zod schema to validate against
52+
* @param input - Unknown input to validate
53+
* @returns Validated and typed input
54+
* @throws {Error} If validation fails, with formatted error messages
55+
*
56+
* @example
57+
* ```typescript
58+
* const schema = z.object({ name: z.string() });
59+
* const validated = validateInput(schema, { name: "test" });
60+
* // Returns: { name: "test" } with TypeScript type inference
61+
* ```
62+
*/
63+
export function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): T {
64+
try {
65+
return schema.parse(input);
66+
} catch (error) {
67+
if (error instanceof z.ZodError) {
68+
const issues = error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
69+
throw new Error(`Validation failed:\n${issues.join('\n')}`);
70+
}
71+
throw error;
72+
}
73+
}

0 commit comments

Comments
 (0)