import { describe, it, expect } from 'vitest'; import { RECIPE_EXTRACTION_SYSTEM_PROMPT, GEMINI_RESPONSE_SCHEMA, extractionResponseSchema, type ExtractionResponse } from '../../src/lib/server/ai/recipe-extraction-prompt'; describe('recipe-extraction-prompt', () => { it('system prompt is in German and mentions Rezept + Zutaten + Zubereitung', () => { const p = RECIPE_EXTRACTION_SYSTEM_PROMPT.toLowerCase(); expect(p).toContain('rezept'); expect(p).toContain('zutaten'); expect(p).toContain('zubereitung'); }); it('Gemini response schema has required top-level keys', () => { expect(GEMINI_RESPONSE_SCHEMA.type).toBeDefined(); expect(Object.keys(GEMINI_RESPONSE_SCHEMA.properties)).toEqual( expect.arrayContaining(['title', 'ingredients', 'steps']) ); }); it('Zod validator accepts a well-formed response', () => { const good: ExtractionResponse = { title: 'Testrezept', servings_default: 4, servings_unit: 'Portionen', prep_time_min: 15, cook_time_min: 30, total_time_min: null, ingredients: [{ quantity: 100, unit: 'g', name: 'Mehl', note: null }], steps: [{ text: 'Mehl in eine Schüssel geben.' }] }; expect(() => extractionResponseSchema.parse(good)).not.toThrow(); }); it('Zod validator rejects missing title', () => { const bad = { servings_default: 4, ingredients: [], steps: [] }; expect(() => extractionResponseSchema.parse(bad)).toThrow(); }); it('Zod validator accepts quantity=null and unit=null', () => { const ok: ExtractionResponse = { title: 'Prise-Rezept', servings_default: null, servings_unit: null, prep_time_min: null, cook_time_min: null, total_time_min: null, ingredients: [{ quantity: null, unit: null, name: 'Salz', note: 'nach Geschmack' }], steps: [{ text: 'Einfach so.' }] }; expect(() => extractionResponseSchema.parse(ok)).not.toThrow(); }); it('Zod validator rejects unexpected extra top-level keys (strict)', () => { const bad = { title: 'x', servings_default: null, servings_unit: null, prep_time_min: null, cook_time_min: null, total_time_min: null, ingredients: [], steps: [], malicious_extra_field: 'pwned' }; expect(() => extractionResponseSchema.parse(bad)).toThrow(); }); });