/** * Unit tests for API definition functionality. * Tests the core defineAPI function and type safety of different API patterns. */ import { describe, it, expect, expectTypeOf } from 'vitest' import { z } from 'zod' import { defineAPI, type API, type StdAPI, type ArgAPI, type RetAPI, type PlainAPI, PlainAPI } from '../src/def' describe('API Definition', () => { describe('defineAPI function', () => { it('should create PlainAPI (no input/output)', () => { const api = defineAPI({ path: '/health' }) expect(api.path).toBe('/health') expect(api.dtoSchema).toBeUndefined() expect(api.voSchema).toBeUndefined() // Type check expectTypeOf(api).toMatchTypeOf() }) it('should create ArgAPI (input only)', () => { const dtoSchema = z.object({ name: z.string(), email: z.string().email() }) const api = defineAPI({ path: '/users/create', dtoSchema }) expect(api.path).toBe('/users/create') expect(api.dtoSchema).toBe(dtoSchema) expect(api.voSchema).toBeUndefined() // Type check expectTypeOf(api).toMatchTypeOf>() }) it('should create RetAPI (output only)', () => { const voSchema = z.object({ id: z.string(), name: z.string(), status: z.string() }) const api = defineAPI({ path: '/server/status', voSchema }) expect(api.path).toBe('/server/status') expect(api.dtoSchema).toBeUndefined() expect(api.voSchema).toBe(voSchema) // Type check expectTypeOf(api).toMatchTypeOf>() }) it('should create StdAPI (both input and output)', () => { const dtoSchema = z.object({ id: z.string() }) const voSchema = z.object({ id: z.string(), name: z.string(), email: z.string() }) const api = defineAPI({ path: '/users/get', dtoSchema, voSchema }) expect(api.path).toBe('/users/get') expect(api.dtoSchema).toBe(dtoSchema) expect(api.voSchema).toBe(voSchema) // Type check expectTypeOf(api).toMatchTypeOf>() }) it('should preserve path exactly as provided', () => { const paths = [ '/api/v1/users', '/health-check', '/admin/settings', '/data/export' ] paths.forEach(path => { const api = defineAPI({ path }) expect(api.path).toBe(path) }) }) it('should work with complex nested schemas', () => { const dtoSchema = z.object({ user: z.object({ id: z.string(), profile: z.object({ name: z.string(), age: z.number().min(0).max(150) }) }), options: z.array(z.string()).optional(), metadata: z.record(z.string(), z.any()).optional() }) const voSchema = z.object({ success: z.boolean(), data: z.object({ userId: z.string(), createdAt: z.string().datetime(), permissions: z.array(z.enum(['read', 'write', 'admin'])) }), errors: z.array(z.string()).optional() }) const api = defineAPI({ path: '/users/complex-operation', dtoSchema, voSchema }) expect(api.path).toBe('/users/complex-operation') expect(api.dtoSchema).toBe(dtoSchema) expect(api.voSchema).toBe(voSchema) // Type check - should be StdAPI expectTypeOf(api).toMatchTypeOf>() }) }) describe('API type inference', () => { it('should infer correct types for different API patterns', () => { // PlainAPI const plainApi = defineAPI({ path: '/ping' }) expectTypeOf().toMatchTypeOf>() // ArgAPI const dtoSchema = z.object({ message: z.string() }) const argApi = defineAPI({ path: '/log', dtoSchema }) expectTypeOf().toMatchTypeOf>() // RetAPI const voSchema = z.object({ timestamp: z.number() }) const retApi = defineAPI({ path: '/time', voSchema }) expectTypeOf().toMatchTypeOf>() // StdAPI const stdApi = defineAPI({ path: '/echo', dtoSchema, voSchema }) expectTypeOf().toMatchTypeOf>() }) it('should maintain schema references', () => { const userSchema = z.object({ name: z.string().min(1), email: z.string().email(), age: z.number().int().min(18) }) const responseSchema = z.object({ id: z.string().uuid(), created: z.boolean() }) const api = defineAPI({ path: '/users', dtoSchema: userSchema, voSchema: responseSchema }) // Schemas should be the exact same objects expect(api.dtoSchema).toBe(userSchema) expect(api.voSchema).toBe(responseSchema) // Should be able to use schema methods expect(api.dtoSchema?.safeParse).toBeDefined() expect(api.voSchema?.safeParse).toBeDefined() }) }) })