UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

657 lines (546 loc) 21.8 kB
import { Type, z } from "alepha"; import { describe, test } from "vitest"; import { FakeProvider } from "../providers/FakeProvider.ts"; describe("FakeProvider", () => { test("generates string values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.string(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result.length).toBeGreaterThan(0); }); test("generates text with size constraints", ({ expect }) => { const fake = new FakeProvider(); const shortText = fake.generate(z.shortText()); expect(typeof shortText).toBe("string"); expect(shortText.length).toBeLessThanOrEqual(64); const regularText = fake.generate(z.text()); expect(typeof regularText).toBe("string"); expect(regularText.length).toBeLessThanOrEqual(255); const longText = fake.generate(z.longText()); expect(typeof longText).toBe("string"); expect(longText.length).toBeLessThanOrEqual(1024); const richText = fake.generate(z.richText()); expect(typeof richText).toBe("string"); expect(richText.length).toBeLessThanOrEqual(65535); }); test("generates number values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.number(); const result = fake.generate(schema); expect(typeof result).toBe("number"); }); test("generates number with constraints", ({ expect }) => { const fake = new FakeProvider(); const schema = z.number().min(10).max(20); const result = fake.generate(schema); expect(result).toBeGreaterThanOrEqual(10); expect(result).toBeLessThanOrEqual(20); }); test("generates integer values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.integer(); const result = fake.generate(schema); expect(typeof result).toBe("number"); expect(Number.isInteger(result)).toBe(true); }); test("generates integer with constraints", ({ expect }) => { const fake = new FakeProvider(); const schema = z.integer().min(5).max(15); const result = fake.generate(schema); expect(Number.isInteger(result)).toBe(true); expect(result).toBeGreaterThanOrEqual(5); expect(result).toBeLessThanOrEqual(15); }); test("generates boolean values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.boolean(); const result = fake.generate(schema); expect(typeof result).toBe("boolean"); }); test("generates UUID", ({ expect }) => { const fake = new FakeProvider(); const schema = z.uuid(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, ); }); test("generates email", ({ expect }) => { const fake = new FakeProvider(); const schema = z.email(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch(/^[^\s@]+@[^\s@]+\.[^\s@]+$/); }); test("generates URL", ({ expect }) => { const fake = new FakeProvider(); const schema = z.string().meta({ format: "url" }); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch(/^https?:\/\//); }); test("generates bigint", ({ expect }) => { const fake = new FakeProvider(); const schema = z.string().meta({ format: "bigint" }); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(/^-?\d+$/.test(result)).toBe(true); }); test("generates E.164 phone number", ({ expect }) => { const fake = new FakeProvider(); const schema = z.e164(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch(/^\+[1-9]\d{1,14}$/); }); test("generates BCP 47 language tag", ({ expect }) => { const fake = new FakeProvider(); const schema = z.bcp47(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch(/^[a-z]{2,3}(?:-[A-Z]{2})?$/); }); test("generates constantCase string", ({ expect }) => { const fake = new FakeProvider(); const schema = z.constantCase(); const result = fake.generate(schema); expect(typeof result).toBe("string"); expect(result).toMatch(/^[A-Z_-]+$/); }); test("generates enum values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.enum(["red", "green", "blue"]); const result = fake.generate(schema); expect(["red", "green", "blue"]).toContain(result); }); test("generates array of values", ({ expect }) => { const fake = new FakeProvider(); // Note: Alepha's z.array() sets maxItems: 1000 by default // FakeProvider caps it to maxArrayLength (default 20) const schema = z.array(z.string()); const result = fake.generate(schema); expect(Array.isArray(result)).toBe(true); expect(result.length).toBeGreaterThanOrEqual(0); expect(result.length).toBeLessThanOrEqual(20); // maxArrayLength default result.forEach((item: unknown) => { expect(typeof item).toBe("string"); }); }); test("generates array with size constraints", ({ expect }) => { const fake = new FakeProvider(); const schema = z.array(z.number()).min(3).max(7); // Generate multiple times to verify constraints are respected const results = Array.from({ length: 20 }, () => fake.generate(schema)); for (const result of results) { expect(Array.isArray(result)).toBe(true); expect(result.length).toBeGreaterThanOrEqual(3); expect(result.length).toBeLessThanOrEqual(7); } // Verify we actually get some arrays with length > 5 (old cap was 5) const hasLargerArray = results.some((r) => r.length > 5); expect(hasLargerArray).toBe(true); }); test("generates object with properties", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ id: z.uuid(), name: z.text(), age: z.integer(), active: z.boolean(), }); const result = fake.generate(schema); expect(typeof result).toBe("object"); expect(result).toHaveProperty("id"); expect(result).toHaveProperty("name"); expect(result).toHaveProperty("age"); expect(result).toHaveProperty("active"); expect(typeof result.id).toBe("string"); expect(typeof result.name).toBe("string"); expect(typeof result.age).toBe("number"); expect(typeof result.active).toBe("boolean"); }); test("generates nested objects", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ user: z.object({ name: z.text(), email: z.email(), }), settings: z.object({ theme: z.enum(["light", "dark"]), notifications: z.boolean(), }), }); const result = fake.generate(schema); expect(typeof result).toBe("object"); expect(typeof result.user).toBe("object"); expect(typeof result.user.name).toBe("string"); expect(typeof result.user.email).toBe("string"); expect(typeof result.settings).toBe("object"); expect(["light", "dark"]).toContain(result.settings.theme); expect(typeof result.settings.notifications).toBe("boolean"); }); test("generates optional values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ required: z.text(), optional: z.text().optional(), }); // Generate multiple times to check both defined and undefined cases const results = Array.from({ length: 20 }, () => fake.generate(schema)); // At least one should be undefined const hasUndefined = results.some((r) => r.optional === undefined); // At least one should be defined const hasDefined = results.some((r) => r.optional !== undefined); expect(hasUndefined || hasDefined).toBe(true); results.forEach((result) => { expect(typeof result.required).toBe("string"); if (result.optional !== undefined) { expect(typeof result.optional).toBe("string"); } }); }); test("generates nullable values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ required: z.text(), nullable: z.text().nullable(), }); // Generate multiple times to check both null and non-null cases const results = Array.from({ length: 20 }, () => fake.generate(schema)); // At least one should be null const hasNull = results.some((r) => r.nullable === null); // At least one should be non-null const hasNonNull = results.some((r) => r.nullable !== null); expect(hasNull || hasNonNull).toBe(true); results.forEach((result) => { expect(typeof result.required).toBe("string"); if (result.nullable !== null) { expect(typeof result.nullable).toBe("string"); } }); }); test("generates union types", ({ expect }) => { const fake = new FakeProvider(); const schema = z.union([z.string(), z.number(), z.boolean()]); const result = fake.generate(schema); const validTypes = ["string", "number", "boolean"]; expect(validTypes).toContain(typeof result); }); test("generates record types", ({ expect }) => { const fake = new FakeProvider(); const schema = z.record(z.string(), z.number()); const result = fake.generate(schema); expect(typeof result).toBe("object"); const entries = Object.entries(result); expect(entries.length).toBeGreaterThan(0); entries.forEach(([key, value]) => { expect(typeof key).toBe("string"); expect(typeof value).toBe("number"); }); }); test("generates tuple types", ({ expect }) => { const fake = new FakeProvider(); const schema = z.tuple([z.string(), z.number(), z.boolean()]); const result = fake.generate(schema); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(3); expect(typeof result[0]).toBe("string"); expect(typeof result[1]).toBe("number"); expect(typeof result[2]).toBe("boolean"); }); test("generates literal values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.const("ACTIVE"); const result = fake.generate(schema); expect(result).toBe("ACTIVE"); }); test("generates null", ({ expect }) => { const fake = new FakeProvider(); const schema = z.null(); const result = fake.generate(schema); expect(result).toBeNull(); }); test("generates undefined", ({ expect }) => { const fake = new FakeProvider(); const schema = z.undefined(); const result = fake.generate(schema); expect(result).toBeUndefined(); }); test("generates void as undefined", ({ expect }) => { const fake = new FakeProvider(); const schema = z.void(); const result = fake.generate(schema); expect(result).toBeUndefined(); }); test("generates any type", ({ expect }) => { const fake = new FakeProvider(); const schema = z.any(); const result = fake.generate(schema); const validTypes = ["string", "number", "boolean"]; expect(validTypes).toContain(typeof result); }); test("generateMany produces multiple values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ id: z.uuid(), name: z.text(), }); const results = fake.generateMany(schema, 5); expect(Array.isArray(results)).toBe(true); expect(results.length).toBe(5); results.forEach((result: { id: unknown; name: unknown }) => { expect(typeof result.id).toBe("string"); expect(typeof result.name).toBe("string"); }); }); test("uses seed for deterministic generation", ({ expect }) => { const schema = z.object({ id: z.uuid(), name: z.text(), age: z.integer(), }); // Generate with same seed - should produce same results const fake1 = new FakeProvider().configure({ seed: 99999 }); const result1a = fake1.generate(schema); const result1b = fake1.generate(schema); // Create new instance with same seed const fake2 = new FakeProvider().configure({ seed: 99999 }); const result2a = fake2.generate(schema); // First result from both instances should match expect(result2a).toEqual(result1a); // But sequential results from same instance should differ expect(result1b).not.toEqual(result1a); }); test("generates different values without seed", ({ expect }) => { const fake = new FakeProvider(); const schema = z.uuid(); const result1 = fake.generate(schema); const result2 = fake.generate(schema); // They should be different (highly unlikely to be the same) expect(result1).not.toBe(result2); }); test("complex nested schema", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ id: z.uuid(), user: z.object({ name: z.shortText(), email: z.email(), age: z.integer().min(18).max(99), phone: z.e164().optional(), }), tags: z.array(z.enum(["urgent", "important", "review"])), metadata: z.record(z.string(), z.any()), status: z.enum(["active", "inactive", "pending"]), createdAt: z.string().meta({ format: "date-time" }), }); const result = fake.generate(schema); expect(typeof result).toBe("object"); expect(typeof result.id).toBe("string"); expect(typeof result.user).toBe("object"); expect(typeof result.user.name).toBe("string"); expect(typeof result.user.email).toBe("string"); expect(typeof result.user.age).toBe("number"); expect(result.user.age).toBeGreaterThanOrEqual(18); expect(result.user.age).toBeLessThanOrEqual(99); expect(Array.isArray(result.tags)).toBe(true); expect(typeof result.metadata).toBe("object"); expect(["active", "inactive", "pending"]).toContain(result.status); expect(typeof result.createdAt).toBe("string"); }); test("valueLabel pattern", ({ expect }) => { const fake = new FakeProvider(); const schema = z.valueLabel(); const result = fake.generate(schema); expect(typeof result).toBe("object"); expect(typeof result.value).toBe("string"); expect(typeof result.label).toBe("string"); expect(result.value).toMatch(/^[A-Z_-]+$/); if (result.description !== undefined) { expect(typeof result.description).toBe("string"); } }); test("context-aware generation for string fields", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ email: z.string(), firstName: z.string(), lastName: z.string(), name: z.string(), username: z.string(), phone: z.string(), address: z.string(), city: z.string(), country: z.string(), company: z.string(), url: z.string(), }); const result = fake.generate(schema); expect(result.email).toMatch(/@/); expect(typeof result.firstName).toBe("string"); expect(typeof result.lastName).toBe("string"); expect(typeof result.name).toBe("string"); expect(typeof result.username).toBe("string"); expect(typeof result.phone).toBe("string"); expect(typeof result.address).toBe("string"); expect(typeof result.city).toBe("string"); expect(typeof result.country).toBe("string"); expect(typeof result.company).toBe("string"); expect(result.url).toMatch(/^https?:\/\//); }); test("context-aware generation handles snake_case field names", ({ expect, }) => { const fake = new FakeProvider(); const schema = z.object({ user_name: z.string(), first_name: z.string(), last_name: z.string(), full_name: z.string(), phone_number: z.string(), email_address: z.string(), }); const result = fake.generate(schema); expect(typeof result.user_name).toBe("string"); expect(typeof result.first_name).toBe("string"); expect(typeof result.last_name).toBe("string"); expect(typeof result.full_name).toBe("string"); expect(typeof result.phone_number).toBe("string"); expect(result.email_address).toMatch(/@/); }); test("context-aware generation handles kebab-case field names", ({ expect, }) => { const fake = new FakeProvider(); const schema = z.object({ "user-name": z.string(), "first-name": z.string(), "last-name": z.string(), "e-mail": z.string(), }); const result = fake.generate(schema); expect(typeof result["user-name"]).toBe("string"); expect(typeof result["first-name"]).toBe("string"); expect(typeof result["last-name"]).toBe("string"); expect(result["e-mail"]).toMatch(/@/); }); test("context-aware generation for number fields", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ age: z.integer(), year: z.integer(), month: z.integer(), day: z.integer(), price: z.number(), user_age: z.integer(), }); const result = fake.generate(schema); expect(result.age).toBeGreaterThanOrEqual(18); expect(result.age).toBeLessThanOrEqual(99); expect(result.year).toBeGreaterThan(1900); expect(result.month).toBeGreaterThanOrEqual(1); expect(result.month).toBeLessThanOrEqual(12); expect(result.day).toBeGreaterThanOrEqual(1); expect(result.day).toBeLessThanOrEqual(31); expect(result.price).toBeGreaterThan(0); expect(result.user_age).toBeGreaterThanOrEqual(18); expect(result.user_age).toBeLessThanOrEqual(99); }); test("context-aware generation respects explicit formats", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ // Even though field is named "email", the uuid format takes precedence email: z.uuid(), }); const result = fake.generate(schema); expect(result.email).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, ); }); test("context-aware generation respects enum values", ({ expect }) => { const fake = new FakeProvider(); const schema = z.object({ // Even though field is named "username", the enum takes precedence username: z.enum(["admin", "user", "guest"]), }); const result = fake.generate(schema); expect(["admin", "user", "guest"]).toContain(result.username); }); test("respects optionalProbability option", ({ expect }) => { // With probability 0, optional should never be undefined const fakeNever = new FakeProvider().configure({ optionalProbability: 0 }); const schema = z.object({ value: z.string().optional() }); const resultsNever = Array.from({ length: 20 }, () => fakeNever.generate(schema), ); expect(resultsNever.every((r) => r.value !== undefined)).toBe(true); // With probability 1, optional should always be undefined const fakeAlways = new FakeProvider().configure({ optionalProbability: 1 }); const resultsAlways = Array.from({ length: 20 }, () => fakeAlways.generate(schema), ); expect(resultsAlways.every((r) => r.value === undefined)).toBe(true); }); test("respects nullableProbability option", ({ expect }) => { // With probability 0, nullable should never be null const fakeNever = new FakeProvider().configure({ nullableProbability: 0 }); const schema = z.object({ value: z.string().nullable() }); const resultsNever = Array.from({ length: 20 }, () => fakeNever.generate(schema), ); expect(resultsNever.every((r) => r.value !== null)).toBe(true); // With probability 1, nullable should always be null const fakeAlways = new FakeProvider().configure({ nullableProbability: 1 }); const resultsAlways = Array.from({ length: 20 }, () => fakeAlways.generate(schema), ); expect(resultsAlways.every((r) => r.value === null)).toBe(true); }); test("respects maxArrayLength option", ({ expect }) => { // With maxArrayLength: 10, arrays should not exceed 10 items const fake = new FakeProvider().configure({ maxArrayLength: 10 }); const schema = z.array(z.number()).min(1).max(100); const results = Array.from({ length: 20 }, () => fake.generate(schema)); for (const result of results) { expect(result.length).toBeLessThanOrEqual(10); } }); test("respects defaultArrayLength option", ({ expect }) => { // With defaultArrayLength: 3, arrays without maxItems should default to 3 // Note: Use TypeBox directly since Alepha's z.array() always sets maxItems: 1000 const fake = new FakeProvider().configure({ defaultArrayLength: 3, maxArrayLength: 100, }); const schema = Type.array(Type.number()); // No minItems/maxItems const results = Array.from({ length: 20 }, () => fake.generate(schema)); for (const result of results) { expect(result.length).toBeLessThanOrEqual(3); } }); test("respects defaultRecordEntries option", ({ expect }) => { const fake = new FakeProvider().configure({ defaultRecordEntries: { min: 5, max: 5 }, }); const schema = z.record(z.string(), z.number()); const result = fake.generate(schema); expect(Object.keys(result).length).toBe(5); }); test("configure() reseeds faker when seed changes", ({ expect }) => { const schema = z.uuid(); const fake = new FakeProvider(); // Generate with default seed const result1 = fake.generate(schema); // Reconfigure with same seed - should reset and produce same result fake.configure({ seed: 12345 }); const result2 = fake.generate(schema); expect(result2).toBe(result1); // Reconfigure with different seed - should produce different result fake.configure({ seed: 99999 }); const result3 = fake.generate(schema); expect(result3).not.toBe(result1); }); });