UNPKG

interface-forge

Version:

A TypeScript library for creating strongly typed mock data factories using Faker.js for test data generation

189 lines (161 loc) 5.75 kB
/** * Example: MaxDepth Behavior with Zod Schemas * * This example demonstrates how to properly use the maxDepth option * with ZodFactory to control recursion depth in nested schemas. */ import { z } from 'zod/v4'; import { ZodFactory } from 'interface-forge/zod'; // Example 1: Understanding the Problem // When maxDepth is reached, the factory returns empty objects {} // This can cause validation errors with required fields console.log('=== Example 1: MaxDepth Problem ==='); const problematicSchema = z.object({ data: z.object({ nested: z.object({ deeper: z.object({ deepest: z.object({ value: z.string(), // Required field }), }), }), }), }); try { const factory = new ZodFactory(problematicSchema, { maxDepth: 2 }); factory.build(); console.log('This should not print - validation should fail'); } catch { console.log('Expected error: Schema validation failed at depth limit'); console.log('Reason: Required fields in objects beyond maxDepth'); } // Example 2: Solution with Optional Fields console.log('\n=== Example 2: Using Optional Fields ==='); const optionalSchema = z.object({ data: z.object({ nested: z.object({ deeper: z .object({ deepest: z .object({ value: z.string(), }) .optional(), // Make deep nesting optional }) .optional(), // Make intermediate levels optional too }), }), }); const optionalFactory = new ZodFactory(optionalSchema, { maxDepth: 2 }); const optionalResult = optionalFactory.build(); console.log('Generated with optional fields:'); console.log(JSON.stringify(optionalResult, null, 2)); // Example 3: Recursive Schemas with Depth Control console.log('\n=== Example 3: Recursive Schemas ==='); // Category tree with self-referencing schema interface Category { description: string; id: string; name: string; subcategories?: Category[]; } const CategorySchema: z.ZodType<Category> = z.lazy(() => z.object({ description: z.string().max(200), id: z.uuid(), name: z.string().min(3).max(50), subcategories: z.array(CategorySchema).optional(), // Optional to handle depth limits }), ); const categoryFactory = new ZodFactory(CategorySchema as any, { maxDepth: 3 }); const categoryTree = categoryFactory.build() as Category; // Helper to count depth function countDepth(category: Category, currentDepth = 0): number { if (!category.subcategories || category.subcategories.length === 0) { return currentDepth; } return Math.max( ...category.subcategories.map((sub) => countDepth(sub, currentDepth + 1), ), ); } console.log('Generated category tree:'); console.log(`Root: ${categoryTree.name}`); console.log(`Total depth: ${countDepth(categoryTree)}`); // Example 4: Different MaxDepth Values console.log('\n=== Example 4: Different MaxDepth Values ==='); interface TreeNode { left?: TreeNode; right?: TreeNode; value: number; } const treeSchema: z.ZodType<TreeNode> = z.lazy(() => z.object({ left: treeSchema.optional(), right: treeSchema.optional(), value: z.number().int().min(1).max(100), }), ); // Test different depths [1, 2, 3, 5].forEach((depth) => { const factory = new ZodFactory(treeSchema as any, { maxDepth: depth }); const tree = factory.build() as TreeNode; function getMaxDepth(node: TreeNode | undefined, current = 0): number { if (!node) { return current; } const leftDepth = node.left ? getMaxDepth(node.left, current + 1) : current; const rightDepth = node.right ? getMaxDepth(node.right, current + 1) : current; return Math.max(leftDepth, rightDepth); } console.log(`maxDepth=${depth}: Actual depth=${getMaxDepth(tree)}`); }); // Example 5: Practical Use Case - Comment Thread console.log('\n=== Example 5: Comment Thread with MaxDepth ==='); interface Comment { author: string; content: string; id: string; replies?: Comment[]; timestamp: Date; } const CommentSchema: z.ZodType<Comment> = z.lazy(() => z.object({ author: z.string(), content: z.string().min(1).max(500), id: z.uuid(), replies: z.array(CommentSchema).max(5).optional(), // Limit replies and make optional timestamp: z.date(), }), ); // Create a comment thread with limited depth const commentFactory = new ZodFactory(CommentSchema as any, { maxDepth: 4, // Allows for main comment + 3 levels of replies }); const commentThread = commentFactory.build() as Comment; function printCommentThread(comment: Comment, indent = ''): void { console.log( `${indent}[${comment.author}]: ${comment.content.slice(0, 50)}...`, ); if (comment.replies) { comment.replies.forEach((reply) => { printCommentThread(reply, `${indent} `); }); } } console.log('Generated comment thread:'); printCommentThread(commentThread); // Example 6: Best Practices Summary console.log('\n=== Best Practices for MaxDepth ==='); console.log( '1. Always use .optional() for nested objects that might hit depth limits', ); console.log('2. Design schemas with natural termination points'); console.log('3. Consider the total depth needed for your use case'); console.log('4. Test with different maxDepth values to ensure proper behavior'); console.log('5. Use lazy schemas (z.lazy) for true recursive structures');