- User.min_quota_pct Int @default(20) + ClaudeWorker.last_quota_pct/last_quota_check_at - Migratie add_worker_quota_gate - lib/schemas/user.ts: minQuotaPctSchema (int, 1-100) - actions/settings.ts: updateMinQuotaPctAction met auth/demo/zod-guard - MinQuotaEditor component met numeric input en DemoTooltip - Settings-pagina: Worker-instellingen sectie Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
const { mockUserUpdate, mockGetIronSession } = vi.hoisted(() => ({
|
|
mockUserUpdate: vi.fn(),
|
|
mockGetIronSession: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }))
|
|
vi.mock('next/headers', () => ({ cookies: vi.fn().mockResolvedValue({}) }))
|
|
vi.mock('iron-session', () => ({ getIronSession: mockGetIronSession }))
|
|
vi.mock('@/lib/session', () => ({ sessionOptions: { cookieName: 'test', password: 'test' } }))
|
|
vi.mock('@/lib/prisma', () => ({
|
|
prisma: { user: { update: mockUserUpdate } },
|
|
}))
|
|
|
|
import { updateMinQuotaPctAction } from '@/actions/settings'
|
|
|
|
const SESSION_USER = { userId: 'user-1', isDemo: false }
|
|
const SESSION_DEMO = { userId: 'demo-1', isDemo: true }
|
|
const SESSION_UNAUTH = { userId: undefined, isDemo: false }
|
|
|
|
describe('updateMinQuotaPctAction', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockUserUpdate.mockResolvedValue({})
|
|
})
|
|
|
|
it('returns error when not authenticated', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_UNAUTH)
|
|
const result = await updateMinQuotaPctAction(20)
|
|
expect(result).toMatchObject({ error: expect.any(String) })
|
|
expect(mockUserUpdate).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('returns 403 error for demo session', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_DEMO)
|
|
const result = await updateMinQuotaPctAction(20)
|
|
expect(result).toMatchObject({ status: 403 })
|
|
expect(mockUserUpdate).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('returns 422 error when value is 0 (below min)', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_USER)
|
|
const result = await updateMinQuotaPctAction(0)
|
|
expect(result).toMatchObject({ status: 422 })
|
|
expect(mockUserUpdate).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('returns 422 error when value is 101 (above max)', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_USER)
|
|
const result = await updateMinQuotaPctAction(101)
|
|
expect(result).toMatchObject({ status: 422 })
|
|
expect(mockUserUpdate).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('saves valid value and returns success', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_USER)
|
|
const result = await updateMinQuotaPctAction(35)
|
|
expect(result).toEqual({ success: true })
|
|
expect(mockUserUpdate).toHaveBeenCalledWith({
|
|
where: { id: 'user-1' },
|
|
data: { min_quota_pct: 35 },
|
|
})
|
|
})
|
|
|
|
it('accepts boundary values 1 and 100', async () => {
|
|
mockGetIronSession.mockResolvedValue(SESSION_USER)
|
|
await updateMinQuotaPctAction(1)
|
|
await updateMinQuotaPctAction(100)
|
|
expect(mockUserUpdate).toHaveBeenCalledTimes(2)
|
|
})
|
|
})
|