import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('$env/dynamic/private', () => ({ env: { BACKEND_URL: 'http://localhost:8080' } })); const mockGet = vi.fn(); const mockPost = vi.fn(); vi.mock('$lib/server/api', () => ({ apiClient: () => ({ GET: mockGet, POST: mockPost }) })); describe('suggestions page — load', () => { let load: any; const mockSuggestions = { suggestions: [ { recipe: { id: 'r1', name: 'Pasta al Limone', effort: 'Easy', cookTimeMin: 25 }, simulatedScore: 9.2 }, { recipe: { id: 'r2', name: 'Hühnchen Curry', effort: 'Medium', cookTimeMin: 45 }, simulatedScore: 6.1 } ] }; const mockWeekPlan = { id: 'plan-1', weekStart: '2026-03-30', status: 'draft', slots: [ { id: 's1', slotDate: '2026-03-30', recipe: { id: 'r3', name: 'Pasta', effort: 'Easy' } } ] }; beforeEach(async () => { mockGet.mockReset(); mockPost.mockReset(); vi.resetModules(); const mod = await import('./+page.server'); load = mod.load; }); it('fetches suggestions for the given plan and day', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: mockSuggestions, error: undefined }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(mockGet).toHaveBeenCalledWith('/v1/week-plans/{id}/suggestions', expect.objectContaining({ params: expect.objectContaining({ path: { id: 'plan-1' } }) })); }); it('returns suggestions list sorted by simulatedScore descending', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: mockSuggestions, error: undefined }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.suggestions[0].recipe.name).toBe('Pasta al Limone'); expect(result.suggestions[1].recipe.name).toBe('Hühnchen Curry'); }); it('returns the selectedDay from URL params', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: mockSuggestions, error: undefined }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.selectedDay).toBe('2026-04-01'); }); it('returns empty suggestions when API fails', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: undefined, error: { status: 500 } }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.suggestions).toEqual([]); }); it('returns week plan slots for context display', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: mockSuggestions, error: undefined }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.weekPlan).toBeDefined(); expect(result.weekPlan.slots).toHaveLength(1); }); it('returns null weekPlan and empty suggestions when week plan not found', async () => { mockGet.mockResolvedValueOnce({ data: undefined, error: { status: 404 } }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30&day=2026-04-01'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.weekPlan).toBeNull(); expect(result.suggestions).toEqual([]); }); it('defaults day to weekStart when no day param provided', async () => { mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined }); mockGet.mockResolvedValueOnce({ data: mockSuggestions, error: undefined }); const url = new URL('http://localhost/planner/suggestions?week=2026-03-30'); const result = await load({ fetch: vi.fn(), url, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result.selectedDay).toBe('2026-03-30'); }); }); describe('suggestions page — pickSuggestion action', () => { let actions: any; beforeEach(async () => { mockGet.mockReset(); mockPost.mockReset(); vi.resetModules(); const mod = await import('./+page.server'); actions = mod.actions; }); it('adds a slot to the week plan via POST and redirects to planner', async () => { mockPost.mockResolvedValue({ data: { id: 's-new', slotDate: '2026-04-01', recipe: {} }, error: undefined }); const formData = new FormData(); formData.set('planId', 'plan-1'); formData.set('recipeId', '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f'); formData.set('slotDate', '2026-04-01'); formData.set('weekStart', '2026-03-30'); try { await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect.unreachable(); } catch (e: any) { expect(e.status).toBe(303); expect(e.location).toBe('/planner?week=2026-03-30'); } expect(mockPost).toHaveBeenCalledWith('/v1/week-plans/{id}/slots', expect.objectContaining({ params: { path: { id: 'plan-1' } }, body: { slotDate: '2026-04-01', recipeId: '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f' } })); }); it('returns error when planId is missing', async () => { const formData = new FormData(); formData.set('planId', ''); formData.set('recipeId', '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f'); formData.set('slotDate', '2026-04-01'); formData.set('weekStart', '2026-03-30'); const result = await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result).toEqual({ success: false, error: 'Fehlende Plan-ID.' }); expect(mockPost).not.toHaveBeenCalled(); }); it('returns error for invalid recipeId format', async () => { const formData = new FormData(); formData.set('planId', 'plan-1'); formData.set('recipeId', 'not-a-uuid'); formData.set('slotDate', '2026-04-01'); formData.set('weekStart', '2026-03-30'); const result = await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result).toEqual({ success: false, error: 'Ungültige Rezept-ID.' }); expect(mockPost).not.toHaveBeenCalled(); }); it('returns error when API fails', async () => { mockPost.mockResolvedValue({ data: undefined, error: { message: 'Server error' } }); const formData = new FormData(); formData.set('planId', 'plan-1'); formData.set('recipeId', '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f'); formData.set('slotDate', '2026-04-01'); formData.set('weekStart', '2026-03-30'); const result = await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result).toEqual({ success: false, error: expect.any(String) }); }); it('returns permission error for member role', async () => { const formData = new FormData(); formData.set('planId', 'plan-1'); formData.set('recipeId', '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f'); formData.set('slotDate', '2026-04-01'); formData.set('weekStart', '2026-03-30'); const result = await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'mitglied' } } }); expect(result).toEqual({ success: false, error: 'Keine Berechtigung.' }); expect(mockPost).not.toHaveBeenCalled(); }); it('returns error for invalid slotDate format', async () => { const formData = new FormData(); formData.set('planId', 'plan-1'); formData.set('recipeId', '1a2b3c4d-1234-1234-1234-1a2b3c4d5e6f'); formData.set('slotDate', 'not-a-date'); formData.set('weekStart', '2026-03-30'); const result = await actions.pickSuggestion({ fetch: vi.fn(), request: { formData: async () => formData }, locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' } } }); expect(result).toEqual({ success: false, error: expect.any(String) }); expect(mockPost).not.toHaveBeenCalled(); }); });