Why: Raw identifiers and mixed settings scopes made project administration difficult and hid authorization boundaries. Evidence: SELF receipts /tmp/project-settings-frontend-full-green-20261014-973d52c1.json and /tmp/project-settings-browser-fixture-final-20261014-973d52c1.json record 284 tests, typecheck, build and 15 real React browser fixture flows. Live composition and formal acceptance remain NOT_RUN. Impact: Add searchable member and document selectors, role filters, versioned duty changes, removal impact review and shared permission details/matrix. Consume server capabilities and settings scopes, preserve personal preferences and default restoration, and discard late responses. Refs: P3/P4/P5/P6/P7, docs/records/2026-10-14-rdm-settings-completion-contract.md
34 lines
1.7 KiB
TypeScript
34 lines
1.7 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { api } from '../src/api'
|
|
import { auth } from '../src/auth'
|
|
|
|
afterEach(() => vi.restoreAllMocks())
|
|
|
|
describe('versioned shared settings API', () => {
|
|
it('sends the loaded project revision and settings in the strict envelope', async () => {
|
|
let url: unknown, init: RequestInit | undefined
|
|
vi.spyOn(auth, 'fetch').mockImplementation(async (path, options) => {
|
|
url = path; init = options
|
|
return new Response(JSON.stringify({ ok: true, projectRevision: 8 }))
|
|
})
|
|
await api.putProjectSettings('host/id', { deliverables: { style: 'formal' } }, 7)
|
|
expect(url).toBe('/api/settings/project/host%2Fid')
|
|
expect(JSON.parse(String(init?.body))).toEqual({ values: { deliverables: { style: 'formal' } }, expectedRevision: 7 })
|
|
})
|
|
it('rejects permission and conflict failures for all settings writers', async () => {
|
|
vi.spyOn(auth, 'fetch').mockResolvedValue(new Response('changed', { status: 409 }))
|
|
await expect(api.putSystemSettings({}, 3)).rejects.toThrow('409')
|
|
vi.spyOn(auth, 'fetch').mockResolvedValue(new Response('denied', { status: 403 }))
|
|
await expect(api.putUserSettings({})).rejects.toThrow('403')
|
|
})
|
|
it('sends genuine inheritance removal without guessing a default value', async () => {
|
|
let init: RequestInit | undefined
|
|
vi.spyOn(auth, 'fetch').mockImplementation(async (_path, options) => {
|
|
init = options
|
|
return new Response(JSON.stringify({ ok: true, projectRevision: 8 }))
|
|
})
|
|
await api.putProjectSettings('host/id', {}, 7, ['deliverables.style'])
|
|
expect(JSON.parse(String(init?.body))).toEqual({ values: {}, expectedRevision: 7, unset: ['deliverables.style'] })
|
|
})
|
|
})
|