From 54df70a44296ff75d4d024a9918cecd58dafb4aa Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Thu, 2 Apr 2026 20:13:02 +0200 Subject: [PATCH] feat(staples): add PATCH proxy server route for ingredient staple toggle Co-Authored-By: Claude Sonnet 4.6 --- .../src/routes/household/staples/+server.ts | 24 +++++++ .../routes/household/staples/server.test.ts | 63 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 frontend/src/routes/household/staples/+server.ts create mode 100644 frontend/src/routes/household/staples/server.test.ts diff --git a/frontend/src/routes/household/staples/+server.ts b/frontend/src/routes/household/staples/+server.ts new file mode 100644 index 0000000..6c5eab4 --- /dev/null +++ b/frontend/src/routes/household/staples/+server.ts @@ -0,0 +1,24 @@ +import { json } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { apiClient } from '$lib/server/api'; + +export const PATCH: RequestHandler = async ({ request, fetch }) => { + const body = await request.json(); + const { id, isStaple } = body; + + if (!id) { + return json({ error: 'id is required' }, { status: 400 }); + } + + const api = apiClient(fetch); + const { error } = await api.PATCH('/v1/ingredients/{id}', { + params: { path: { id } }, + body: { isStaple } + }); + + if (error) { + return json({ error: 'Failed to update ingredient' }, { status: 500 }); + } + + return new Response(null, { status: 204 }); +}; diff --git a/frontend/src/routes/household/staples/server.test.ts b/frontend/src/routes/household/staples/server.test.ts new file mode 100644 index 0000000..36d7045 --- /dev/null +++ b/frontend/src/routes/household/staples/server.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('$env/dynamic/private', () => ({ + env: { BACKEND_URL: 'http://localhost:8080' } +})); + +const mockPatch = vi.fn(); +vi.mock('$lib/server/api', () => ({ + apiClient: () => ({ PATCH: mockPatch }) +})); + +describe('household staples PATCH handler', () => { + let PATCH: any; + + beforeEach(async () => { + mockPatch.mockReset(); + const mod = await import('./+server'); + PATCH = mod.PATCH; + }); + + function createRequest(body: object) { + return { + request: { + json: () => Promise.resolve(body) + }, + fetch: vi.fn() + } as any; + } + + it('calls backend PATCH /v1/ingredients/{id} with isStaple', async () => { + mockPatch.mockResolvedValue({ data: {}, error: undefined }); + + await PATCH(createRequest({ id: 'ing-1', isStaple: true })); + + expect(mockPatch).toHaveBeenCalledWith('/v1/ingredients/{id}', { + params: { path: { id: 'ing-1' } }, + body: { isStaple: true } + }); + }); + + it('returns 204 on success', async () => { + mockPatch.mockResolvedValue({ data: {}, error: undefined }); + + const response = await PATCH(createRequest({ id: 'ing-1', isStaple: true })); + + expect(response.status).toBe(204); + }); + + it('returns 500 when backend returns an error', async () => { + mockPatch.mockResolvedValue({ data: undefined, error: { status: 500, message: 'error' } }); + + const response = await PATCH(createRequest({ id: 'ing-1', isStaple: false })); + + expect(response.status).toBe(500); + }); + + it('returns 400 when id is missing', async () => { + const response = await PATCH(createRequest({ isStaple: true })); + + expect(response.status).toBe(400); + expect(mockPatch).not.toHaveBeenCalled(); + }); +});