UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

200 lines (154 loc) 6.88 kB
import "reflect-metadata"; import { SchemaReflector } from "../../core/SchemaReflector"; import { Entity, Property, Id, Min, Max, Email, MinLength } from "../../core/decorators"; describe("SchemaReflector - Validation Integration", () => { let schemaReflector: SchemaReflector; beforeEach(() => { schemaReflector = new SchemaReflector(); }); @Entity({ name: "User" }) class User { @Id() id: string; @Property({ required: true }) @MinLength(2) name: string; @Property({ required: true }) @Min({ value: 0 }) @Max({ value: 120 }) age: number; @Property({ required: true }) @Email() email: string; @Property() bio?: string; constructor(id: string, name: string, age: number, email: string, bio?: string) { this.id = id; this.name = name; this.age = age; this.email = email; this.bio = bio; } } describe("validateEntityWithRules", () => { it("should validate a completely valid entity", async () => { const user = new User("1", "John Doe", 25, "john@example.com", "Software developer"); const result = await schemaReflector.validateEntityWithRules(user); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should catch both required field violations and validation rule violations", async () => { const user = new User("1", "", 150, "invalid-email"); const result = await schemaReflector.validateEntityWithRules(user); expect(result.isValid).toBe(false); expect(result.errors.length).toBeGreaterThan(0); // Should have validation rule errors const ruleTypes = result.errors.map(e => e.rule); expect(ruleTypes).toContain("minLength"); // name too short expect(ruleTypes).toContain("max"); // age too high expect(ruleTypes).toContain("email"); // invalid email }); it("should catch missing required fields", async () => { const user = new User("1", "John", 25, "john@example.com"); // Simulate missing required field (user as any).name = undefined; const result = await schemaReflector.validateEntityWithRules(user); expect(result.isValid).toBe(false); expect(result.errors.some(e => e.rule === "required")).toBe(true); }); it("should allow optional fields to be empty while validating provided values", async () => { const user = new User("1", "John Doe", 25, "john@example.com"); // bio is optional and not set const result = await schemaReflector.validateEntityWithRules(user); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); }); describe("validateProperty", () => { it("should validate individual properties correctly", async () => { const user = new User("1", "John Doe", 25, "john@example.com"); const nameResult = await schemaReflector.validateProperty(user, "name"); const ageResult = await schemaReflector.validateProperty(user, "age"); const emailResult = await schemaReflector.validateProperty(user, "email"); expect(nameResult.isValid).toBe(true); expect(ageResult.isValid).toBe(true); expect(emailResult.isValid).toBe(true); }); it("should catch individual property validation failures", async () => { const user = new User("1", "J", 150, "bad-email"); const nameResult = await schemaReflector.validateProperty(user, "name"); const ageResult = await schemaReflector.validateProperty(user, "age"); const emailResult = await schemaReflector.validateProperty(user, "email"); expect(nameResult.isValid).toBe(false); expect(nameResult.errors[0].rule).toBe("minLength"); expect(ageResult.isValid).toBe(false); expect(ageResult.errors[0].rule).toBe("max"); expect(emailResult.isValid).toBe(false); expect(emailResult.errors[0].rule).toBe("email"); }); it("should return valid for properties without validation rules", async () => { const user = new User("1", "John Doe", 25, "john@example.com"); const idResult = await schemaReflector.validateProperty(user, "id"); expect(idResult.isValid).toBe(true); expect(idResult.errors).toHaveLength(0); }); }); describe("Backward Compatibility", () => { it("should maintain backward compatibility with old validateEntity method", () => { const user = new User("1", "John Doe", 25, "john@example.com"); const result = schemaReflector.validateEntity(user); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should catch required field violations in old validateEntity method", () => { const user = new User("1", "John Doe", 25, "john@example.com"); (user as any).name = undefined; const result = schemaReflector.validateEntity(user); expect(result.valid).toBe(false); expect(result.errors.length).toBeGreaterThan(0); expect(result.errors[0]).toContain("Required property 'name' is missing"); }); }); describe("Complex Validation Scenarios", () => { @Entity() class Product { @Id() id: string; @Property({ required: true }) @MinLength(3) name: string; @Property({ required: true }) @Min({ value: 0.01, message: "Price must be greater than 0" }) @Max({ value: 999999.99, message: "Price too high" }) price: number; @Property() @MinLength(10, "Description must be at least 10 characters") description?: string; constructor(id: string, name: string, price: number, description?: string) { this.id = id; this.name = name; this.price = price; this.description = description; } } it("should provide detailed error messages", async () => { const product = new Product("1", "AB", 0, "Short"); const result = await schemaReflector.validateEntityWithRules(product); expect(result.isValid).toBe(false); const nameError = result.errors.find(e => e.property === "name"); const priceError = result.errors.find(e => e.property === "price"); const descError = result.errors.find(e => e.property === "description"); expect(nameError).toBeDefined(); expect(priceError).toBeDefined(); expect(priceError?.message).toBe("Price must be greater than 0"); expect(descError).toBeDefined(); expect(descError?.message).toBe("Description must be at least 10 characters"); }); it("should handle edge cases gracefully", async () => { const product = new Product("1", "Valid Product", 99.99); // description is optional and not provided const result = await schemaReflector.validateEntityWithRules(product); expect(result.isValid).toBe(true); }); }); });