From 05476ecaab32ad6865ab37e1d4562bfdaaa93695 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Fri, 10 Apr 2026 19:57:13 +0200 Subject: [PATCH] fix(members/invites): unwrap ApiResponse before returning to client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /members/invites was returning the full ApiResponseInviteResponse wrapper. The client set activeInvite directly from the response body, so shareUrl/inviteCode/expiresAt were missing (nested under .data). Fixed to return data?.data — the inner InviteResponse — matching the shape that InvitePanel and page.server.ts already expect. Co-Authored-By: Claude Sonnet 4.6 --- .../routes/(app)/members/invites/+server.ts | 2 +- .../(app)/members/invites/server.test.ts | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 frontend/src/routes/(app)/members/invites/server.test.ts diff --git a/frontend/src/routes/(app)/members/invites/+server.ts b/frontend/src/routes/(app)/members/invites/+server.ts index 76d1f44..a15be65 100644 --- a/frontend/src/routes/(app)/members/invites/+server.ts +++ b/frontend/src/routes/(app)/members/invites/+server.ts @@ -5,5 +5,5 @@ import { apiClient } from '$lib/server/api'; export const POST: RequestHandler = async ({ fetch }) => { const api = apiClient(fetch); const { data, response } = await api.POST('/v1/households/mine/invites'); - return json(data, { status: response?.status ?? 201 }); + return json(data?.data, { status: response?.status ?? 201 }); }; diff --git a/frontend/src/routes/(app)/members/invites/server.test.ts b/frontend/src/routes/(app)/members/invites/server.test.ts new file mode 100644 index 0000000..ac7c3b6 --- /dev/null +++ b/frontend/src/routes/(app)/members/invites/server.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('$env/dynamic/private', () => ({ env: { BACKEND_URL: 'http://localhost:8080' } })); + +const mockPost = vi.fn(); +vi.mock('$lib/server/api', () => ({ + apiClient: () => ({ POST: mockPost }) +})); + +describe('invites server route', () => { + let POST: any; + + beforeEach(async () => { + mockPost.mockReset(); + vi.resetModules(); + const mod = await import('./+server'); + POST = mod.POST; + }); + + it('POST returns unwrapped InviteResponse', async () => { + const invite = { inviteCode: 'ABC123', shareUrl: 'https://x.com/join/ABC123', expiresAt: '2026-12-01T00:00:00Z' }; + mockPost.mockResolvedValue({ + data: { status: 'success', data: invite }, + response: { status: 200 } + }); + const event = { fetch: vi.fn() } as any; + const res = await POST(event); + const body = await res.json(); + expect(body.inviteCode).toBe('ABC123'); + expect(body.shareUrl).toBe('https://x.com/join/ABC123'); + expect(body.expiresAt).toBe('2026-12-01T00:00:00Z'); + }); +});