import { signupSchema, signinSchema, addPantryItemSchema, updatePantryItemSchema, addShoppingListItemSchema, updateShoppingListItemSchema, addRecipesToListSchema, recipeQuerySchema, syncQuerySchema } from '../../utils/validators'; describe('Validators', () => { describe('signupSchema', () => { it('accepts valid signup data', () => { const result = signupSchema.safeParse({ email: 'user@example.com', password: 'password123', name: 'Jane' }); expect(result.success).toBe(true); }); it('rejects invalid email', () => { const result = signupSchema.safeParse({ email: 'not-an-email', password: 'password123', name: 'Jane' }); expect(result.success).toBe(false); }); it('rejects short password', () => { const result = signupSchema.safeParse({ email: 'user@example.com', password: 'short', name: 'Jane' }); expect(result.success).toBe(false); }); it('rejects non-alphanumeric password', () => { const result = signupSchema.safeParse({ email: 'user@example.com', password: 'pass!word', name: 'Jane' }); expect(result.success).toBe(false); }); it('rejects empty name', () => { const result = signupSchema.safeParse({ email: 'user@example.com', password: 'password123', name: '' }); expect(result.success).toBe(false); }); }); describe('addPantryItemSchema', () => { it('accepts valid pantry item', () => { const result = addPantryItemSchema.safeParse({ item_name: 'Flour', quantity: 5 }); expect(result.success).toBe(true); }); it('rejects zero quantity', () => { const result = addPantryItemSchema.safeParse({ item_name: 'Flour', quantity: 0 }); expect(result.success).toBe(false); }); it('rejects negative quantity', () => { const result = addPantryItemSchema.safeParse({ item_name: 'Flour', quantity: -1 }); expect(result.success).toBe(false); }); it('rejects fractional quantity', () => { const result = addPantryItemSchema.safeParse({ item_name: 'Flour', quantity: 1.5 }); expect(result.success).toBe(false); }); it('rejects empty item name', () => { const result = addPantryItemSchema.safeParse({ item_name: '', quantity: 5 }); expect(result.success).toBe(false); }); }); describe('updatePantryItemSchema', () => { it('accepts valid update', () => { const result = updatePantryItemSchema.safeParse({ quantity: 3 }); expect(result.success).toBe(true); }); it('accepts update with last_modified', () => { const result = updatePantryItemSchema.safeParse({ quantity: 3, last_modified: '2024-01-15T10:30:00.000Z' }); expect(result.success).toBe(true); }); }); describe('addShoppingListItemSchema', () => { it('accepts valid item', () => { const result = addShoppingListItemSchema.safeParse({ item_name: 'Milk', quantity: 2, unit: 'cups' }); expect(result.success).toBe(true); }); it('rejects invalid unit', () => { const result = addShoppingListItemSchema.safeParse({ item_name: 'Milk', quantity: 2, unit: 'gallons' }); expect(result.success).toBe(false); }); it('accepts fractional quantity', () => { const result = addShoppingListItemSchema.safeParse({ item_name: 'Flour', quantity: 2.5, unit: 'cups' }); expect(result.success).toBe(true); }); }); describe('updateShoppingListItemSchema', () => { it('accepts partial update', () => { const result = updateShoppingListItemSchema.safeParse({ checked_off: true }); expect(result.success).toBe(true); }); it('rejects empty object', () => { const result = updateShoppingListItemSchema.safeParse({}); expect(result.success).toBe(false); }); }); describe('addRecipesToListSchema', () => { it('accepts valid recipe ids', () => { const result = addRecipesToListSchema.safeParse({ recipe_ids: ['123e4567-e89b-12d3-a456-426614174000'], scale_factor: 2, }); expect(result.success).toBe(true); }); it('rejects empty recipe_ids', () => { const result = addRecipesToListSchema.safeParse({ recipe_ids: [] }); expect(result.success).toBe(false); }); it('rejects scale_factor > 3', () => { const result = addRecipesToListSchema.safeParse({ recipe_ids: ['123e4567-e89b-12d3-a456-426614174000'], scale_factor: 4, }); expect(result.success).toBe(false); }); it('defaults scale_factor to 1', () => { const result = addRecipesToListSchema.safeParse({ recipe_ids: ['123e4567-e89b-12d3-a456-426614174000'], }); expect(result.success).toBe(true); if (result.success) expect(result.data.scale_factor).toBe(1); }); }); describe('recipeQuerySchema', () => { it('defaults to all filter, page 1, limit 20', () => { const result = recipeQuerySchema.safeParse({}); expect(result.success).toBe(true); if (result.success) { expect(result.data.filter).toBe('all'); expect(result.data.page).toBe(1); expect(result.data.limit).toBe(20); } }); it('rejects limit > 50', () => { const result = recipeQuerySchema.safeParse({ limit: '51' }); expect(result.success).toBe(false); }); }); describe('syncQuerySchema', () => { it('accepts valid ISO timestamp', () => { const result = syncQuerySchema.safeParse({ since: '2024-01-15T10:30:00.000Z' }); expect(result.success).toBe(true); }); it('rejects invalid timestamp', () => { const result = syncQuerySchema.safeParse({ since: 'not-a-date' }); expect(result.success).toBe(false); }); }); });