fortify-schema
Version:
TypeScript interface-like schema validation system that's easier to use than Zod
248 lines (203 loc) • 6.17 kB
text/typescript
import { Interface, Make } from '../index';
describe('Interface Schema', () => {
describe('Basic Types', () => {
it('should validate string types', () => {
const schema = Interface({
name: 'string',
bio: 'string?'
});
const result = schema.safeParse({
name: 'John Doe'
});
expect(result.success).toBe(true);
expect(result.data).toEqual({ name: 'John Doe' });
});
it('should validate number types', () => {
const schema = Interface({
age: 'number',
score: 'number?'
});
const result = schema.safeParse({
age: 25
});
expect(result.success).toBe(true);
expect(result.data).toEqual({ age: 25 });
});
it('should validate boolean types', () => {
const schema = Interface({
active: 'boolean',
verified: 'boolean?'
});
const result = schema.safeParse({
active: true
});
expect(result.success).toBe(true);
expect(result.data).toEqual({ active: true });
});
});
describe('Format Validation', () => {
it('should validate email format', () => {
const schema = Interface({
email: 'email'
});
const validResult = schema.safeParse({
email: 'user@example.com'
});
const invalidResult = schema.safeParse({
email: 'invalid-email'
});
expect(validResult.success).toBe(true);
expect(invalidResult.success).toBe(false);
});
it('should validate URL format', () => {
const schema = Interface({
website: 'url'
});
const validResult = schema.safeParse({
website: 'https://example.com'
});
const invalidResult = schema.safeParse({
website: 'not-a-url'
});
expect(validResult.success).toBe(true);
expect(invalidResult.success).toBe(false);
});
});
describe('Array Types', () => {
it('should validate string arrays', () => {
const schema = Interface({
tags: 'string[]',
categories: 'string[]?'
});
const result = schema.safeParse({
tags: ['javascript', 'typescript']
});
expect(result.success).toBe(true);
expect(result.data).toEqual({
tags: ['javascript', 'typescript']
});
});
it('should validate number arrays', () => {
const schema = Interface({
scores: 'number[]'
});
const result = schema.safeParse({
scores: [85, 92, 78]
});
expect(result.success).toBe(true);
expect(result.data).toEqual({
scores: [85, 92, 78]
});
});
});
describe('Safe Constants', () => {
it('should validate constant values', () => {
const schema = Interface({
version: Make.const('1.0'),
status: Make.const(200),
enabled: Make.const(true)
});
const validResult = schema.safeParse({
version: '1.0',
status: 200,
enabled: true
});
const invalidResult = schema.safeParse({
version: '2.0', // Wrong version
status: 200,
enabled: true
});
expect(validResult.success).toBe(true);
expect(invalidResult.success).toBe(false);
expect(invalidResult.errors).toContain('version: Expected constant value 1.0, got 2.0');
});
});
describe('Union Types', () => {
it('should validate union values', () => {
const schema = Interface({
status: Make.union('pending', 'accepted', 'rejected'),
priority: Make.union('low', 'medium', 'high')
});
const validResult = schema.safeParse({
status: 'pending',
priority: 'high'
});
const invalidResult = schema.safeParse({
status: 'invalid_status',
priority: 'high'
});
expect(validResult.success).toBe(true);
expect(invalidResult.success).toBe(false);
expect(invalidResult.errors[0]).toContain('Expected one of: pending, accepted, rejected');
});
it('should validate optional union values', () => {
const schema = Interface({
type: Make.unionOptional('standard', 'express', 'overnight')
});
const validResult = schema.safeParse({
type: 'express'
});
const validEmptyResult = schema.safeParse({});
expect(validResult.success).toBe(true);
expect(validEmptyResult.success).toBe(true);
});
});
describe('Nested Objects', () => {
it('should validate nested objects', () => {
const schema = Interface({
user: {
name: 'string',
email: 'email',
profile: {
bio: 'string?',
avatar: 'url?'
}
}
});
const result = schema.safeParse({
user: {
name: 'John Doe',
email: 'john@example.com',
profile: {
bio: 'Software developer'
}
}
});
expect(result.success).toBe(true);
});
});
describe('Error Handling', () => {
it('should provide detailed error messages', () => {
const schema = Interface({
id: 'number',
email: 'email',
status: Make.union('active', 'inactive')
});
const result = schema.safeParse({
id: 'not-a-number',
email: 'invalid-email',
status: 'invalid-status'
});
expect(result.success).toBe(false);
expect(result.errors).toHaveLength(3);
expect(result.errors[0]).toContain('id: Expected number');
expect(result.errors[1]).toContain('email: Invalid email format');
expect(result.errors[2]).toContain('status: Expected one of: active, inactive');
});
});
describe('Strict Mode', () => {
it('should reject extra properties in strict mode', () => {
const schema = Interface({
id: 'number',
name: 'string'
}).strict();
const result = schema.safeParse({
id: 1,
name: 'John',
extraField: 'not allowed'
});
expect(result.success).toBe(false);
expect(result.errors[0]).toContain('Unexpected properties: extraField');
});
});
});