Files
accounted/app/api/agent/__tests__/profile.test.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

150 lines
5.4 KiB
TypeScript

/**
* Tests for GET/PATCH /api/agent/profile and POST /api/agent/profile/verify.
*
* Covers the role model: reads allow any member, mutations (PATCH, verify)
* refuse viewers — the same rule verify always had, now enforced on PATCH too.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { NextResponse } from 'next/server'
import { createQueuedMockSupabase, createMockRequest, parseJsonResponse } from '@/tests/helpers'
const { supabase, enqueue, reset } = createQueuedMockSupabase()
const requireAuthMock = vi.fn()
vi.mock('@/lib/auth/require-auth', () => ({
requireAuth: (...args: unknown[]) => requireAuthMock(...args),
}))
vi.mock('@/lib/company/context', () => ({
getActiveCompanyId: vi.fn().mockResolvedValue('company-1'),
requireCompanyId: vi.fn().mockResolvedValue('company-1'),
}))
import { GET, PATCH } from '../profile/route'
import { POST as VERIFY } from '../profile/verify/route'
const routeParams = { params: Promise.resolve({}) }
beforeEach(() => {
vi.clearAllMocks()
reset()
requireAuthMock.mockResolvedValue({ user: { id: 'user-1' }, supabase, error: null })
})
describe('GET /api/agent/profile', () => {
it('returns 401 when not authenticated', async () => {
requireAuthMock.mockResolvedValue({
user: null,
supabase,
error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }),
})
const res = await GET(createMockRequest('/api/agent/profile'), routeParams)
expect(res.status).toBe(401)
})
it('returns 403 when the caller is not a member of the target company', async () => {
enqueue({ data: null }) // membership lookup
const { status, body } = await parseJsonResponse<{ error: { code: string } }>(
await GET(createMockRequest('/api/agent/profile'), routeParams)
)
expect(status).toBe(403)
expect(body.error.code).toBe('NOT_COMPANY_MEMBER')
})
it('returns the profile for a member (viewers may read)', async () => {
enqueue({ data: { role: 'viewer' } })
enqueue({ data: { company_id: 'company-1', profile_summary: 'Konsultbolag' } })
const { status, body } = await parseJsonResponse<{ data: { profile_summary: string } }>(
await GET(createMockRequest('/api/agent/profile'), routeParams)
)
expect(status).toBe(200)
expect(body.data.profile_summary).toBe('Konsultbolag')
})
})
describe('PATCH /api/agent/profile', () => {
it('refuses a viewer with 403 (profile mutation)', async () => {
enqueue({ data: { role: 'viewer' } })
const req = createMockRequest('/api/agent/profile', {
method: 'PATCH',
body: { profile_summary: 'Nytt sammandrag' },
})
const { status, body } = await parseJsonResponse<{ error: { code: string } }>(
await PATCH(req, routeParams)
)
expect(status).toBe(403)
expect(body.error.code).toBe('WRITE_PERMISSION_REQUIRED')
})
it('returns 404 when the company has no agent_profile row', async () => {
enqueue({ data: { role: 'admin' } })
enqueue({ data: null }) // current profile lookup
const req = createMockRequest('/api/agent/profile', {
method: 'PATCH',
body: { profile_summary: 'Nytt sammandrag' },
})
const { status, body } = await parseJsonResponse<{ error: { code: string } }>(
await PATCH(req, routeParams)
)
expect(status).toBe(404)
expect(body.error.code).toBe('AGENT_PROFILE_NOT_FOUND')
})
it('returns 400 when the body contains nothing to update', async () => {
enqueue({ data: { role: 'admin' } })
enqueue({ data: { field_overrides: null } })
const req = createMockRequest('/api/agent/profile', { method: 'PATCH', body: {} })
const { status, body } = await parseJsonResponse<{ error: { code: string } }>(
await PATCH(req, routeParams)
)
expect(status).toBe(400)
expect(body.error.code).toBe('NOTHING_TO_UPDATE')
})
it('merges field_overrides and updates the profile', async () => {
enqueue({ data: { role: 'admin' } })
enqueue({ data: { field_overrides: { old: { value: 1, overridden_at: 'x' } } } })
enqueue({ data: { company_id: 'company-1', profile_summary: 'Uppdaterad' } })
const req = createMockRequest('/api/agent/profile', {
method: 'PATCH',
body: { profile_summary: 'Uppdaterad', field_overrides: { vat_period: 'quarterly' } },
})
const { status, body } = await parseJsonResponse<{ data: { profile_summary: string } }>(
await PATCH(req, routeParams)
)
expect(status).toBe(200)
expect(body.data.profile_summary).toBe('Uppdaterad')
})
})
describe('POST /api/agent/profile/verify', () => {
it('refuses a viewer with 403', async () => {
enqueue({ data: { role: 'viewer' } })
const req = createMockRequest('/api/agent/profile/verify', { method: 'POST', body: {} })
const { status, body } = await parseJsonResponse<{ error: { code: string } }>(
await VERIFY(req, routeParams)
)
expect(status).toBe(403)
expect(body.error.code).toBe('WRITE_PERMISSION_REQUIRED')
})
it('stamps verified_at for a non-viewer member', async () => {
enqueue({ data: { role: 'owner' } })
enqueue({ data: { company_id: 'company-1', verified_at: '2026-07-03T00:00:00Z', verified_by_user_id: 'user-1' } })
const req = createMockRequest('/api/agent/profile/verify', { method: 'POST', body: {} })
const { status, body } = await parseJsonResponse<{ data: { verified_by_user_id: string } }>(
await VERIFY(req, routeParams)
)
expect(status).toBe(200)
expect(body.data.verified_by_user_id).toBe('user-1')
})
})