Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions apps/sim/app/api/users/me/settings/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @vitest-environment node
*/
import { createMockRequest, dbChainMockFns, resetDbChainMock } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))

vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))

import { PATCH } from '@/app/api/users/me/settings/route'

describe('PATCH /api/users/me/settings', () => {
beforeEach(() => {
vi.clearAllMocks()
resetDbChainMock()
mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
})

it('reports success when the write lands', async () => {
const response = await PATCH(createMockRequest('PATCH', { theme: 'dark' }))

expect(response.status).toBe(200)
expect(await response.json()).toEqual({ success: true })
})

/**
* The regression this guards: the catch answered `{ success: true }` with 200, so
* `useUpdateGeneralSetting`'s optimistic rollback in `onError` could never run —
* a failed write showed as applied until the next refetch, including for
* consent-shaped settings the user believes they changed.
*/
it('reports failure when the write throws', async () => {
dbChainMockFns.insert.mockImplementationOnce(() => {
throw new Error('connection terminated unexpectedly')
})

const response = await PATCH(createMockRequest('PATCH', { theme: 'dark' }))

expect(response.status).toBe(500)
expect(await response.json()).not.toMatchObject({ success: true })
})
})
6 changes: 5 additions & 1 deletion apps/sim/app/api/users/me/settings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export const PATCH = withRouteHandler(async (request: NextRequest) => {
return NextResponse.json({ success: true }, { status: 200 })
} catch (error: any) {
logger.error(`[${requestId}] Settings update error`, error)
return NextResponse.json({ success: true }, { status: 200 })
/* The client mutation is optimistic: it writes the new value into the cache in
`onMutate` and restores it in `onError`. Answering 200 here left that rollback
unreachable, so a failed write showed as applied until the next refetch —
including for consent-shaped settings the user believes they changed. */
return NextResponse.json({ error: 'Failed to update settings' }, { status: 500 })
}
})
Loading