Unify Settings navigation, credential drafts, translated form validation, and appearance controls. Add explicit WebMCP access modes and searchable MCP tool permissions while preserving execution and credential safeguards.
112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import { expect, test } from 'bun:test'
|
|
|
|
import { effectScope, ref } from 'vue'
|
|
|
|
import { useStorageSettings } from '@/app/integrations/storage/settings/use'
|
|
import type { CredentialManager } from '@/app/settings/credentials/types'
|
|
|
|
function fixture() {
|
|
const preferences = ref<Record<string, string>>({
|
|
endpoint: 'https://old.example.com',
|
|
bucket: 'designs',
|
|
region: 'auto'
|
|
})
|
|
const writes: string[] = []
|
|
const tests: Record<string, string>[] = []
|
|
const manager: CredentialManager = {
|
|
backend: 'memory',
|
|
availability: async () => 'available',
|
|
status: async () => 'configured',
|
|
set: async (reference) => {
|
|
writes.push(`set:${reference.field}`)
|
|
},
|
|
clear: async (reference) => {
|
|
writes.push(`clear:${reference.field}`)
|
|
}
|
|
}
|
|
return {
|
|
preferences,
|
|
writes,
|
|
tests,
|
|
services: {
|
|
readPreferences: () => ({ ...preferences.value }),
|
|
writePreference: (_provider: string, field: string, value: string) => {
|
|
preferences.value = { ...preferences.value, [field]: value }
|
|
writes.push(`preference:${field}`)
|
|
},
|
|
statuses: async () => ({
|
|
'access-key-id': 'configured' as const,
|
|
'secret-access-key': 'configured' as const
|
|
}),
|
|
manager,
|
|
test: async (_provider: string, values: Record<string, string>) => {
|
|
tests.push(values)
|
|
return { ok: true, message: 'Connected' }
|
|
},
|
|
resume: async () => undefined
|
|
}
|
|
}
|
|
}
|
|
|
|
test('testing storage drafts and cancelling never persist settings', async () => {
|
|
const f = fixture()
|
|
const scope = effectScope()
|
|
const credentials = ref<Record<string, string>>({})
|
|
try {
|
|
const state = scope.run(() => useStorageSettings(credentials, f.services))
|
|
if (!state) throw new Error('Missing scope')
|
|
state.preferenceDrafts.value.endpoint = 'https://draft.example.com'
|
|
credentials.value['secret-access-key'] = 'test-only-draft'
|
|
expect(state.dirty.value).toBe(true)
|
|
expect(await state.testConnection()).toEqual({ ok: true, message: 'Connected' })
|
|
expect(f.tests[0]?.endpoint).toBe('https://draft.example.com')
|
|
expect(f.writes).toEqual([])
|
|
expect(f.preferences.value.endpoint).toBe('https://old.example.com')
|
|
state.cancel()
|
|
expect(state.dirty.value).toBe(false)
|
|
expect(credentials.value).toEqual({})
|
|
expect(state.preferenceDrafts.value.endpoint).toBe('https://old.example.com')
|
|
} finally {
|
|
scope.stop()
|
|
}
|
|
})
|
|
|
|
test('credential clearing is staged until Save', async () => {
|
|
const f = fixture()
|
|
const scope = effectScope()
|
|
try {
|
|
const state = scope.run(() => useStorageSettings(ref({}), f.services))
|
|
if (!state) throw new Error('Missing scope')
|
|
state.clearCredential('secret-access-key')
|
|
expect(f.writes).toEqual([])
|
|
expect(state.dirty.value).toBe(true)
|
|
expect(await state.save()).toBe(true)
|
|
expect(f.writes).toContain('clear:secret-access-key')
|
|
expect(state.dirty.value).toBe(false)
|
|
} finally {
|
|
scope.stop()
|
|
}
|
|
})
|
|
|
|
test('failed storage saves retain replacement drafts and expose the failure', async () => {
|
|
const f = fixture()
|
|
f.services.manager.set = async () => {
|
|
throw new Error('Credential store locked')
|
|
}
|
|
const scope = effectScope()
|
|
const credentials = ref<Record<string, string>>({})
|
|
try {
|
|
const state = scope.run(() => useStorageSettings(credentials, f.services))
|
|
if (!state) throw new Error('Missing scope')
|
|
credentials.value['secret-access-key'] = 'test-only-replacement'
|
|
expect(await state.save()).toBe(false)
|
|
expect(state.error.value).toBe('Credential store locked')
|
|
expect(state.busy.value).toBe(false)
|
|
expect(credentials.value['secret-access-key']).toBe('test-only-replacement')
|
|
scope.stop()
|
|
expect(credentials.value).toEqual({})
|
|
} finally {
|
|
scope.stop()
|
|
}
|
|
})
|