fix(web): address settings document review
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* invalidation frames (settings/credentials/models changed).
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
@@ -235,7 +235,7 @@ describe('settings domain', () => {
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
const value = expectOk(await api.settings.describe(request({})))
|
||||
expect(value.writable).toBe(true)
|
||||
expect(value.documentPath).toBe('/tmp/custom-settings.yaml')
|
||||
expect(value.hasDocument).toBe(true)
|
||||
expect(value.namespaces).toHaveLength(1)
|
||||
const view = value.namespaces[0]!
|
||||
expect(view.ns).toBe('llm-deepseek')
|
||||
@@ -270,11 +270,40 @@ describe('settings domain', () => {
|
||||
it('refuses to open settings when the provider has no local document', async () => {
|
||||
const ctx = await harness()
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
expect(expectOk(await api.settings.describe(request({}))).hasDocument).toBe(false)
|
||||
const error = expectErr(await api.settings.openDocument(request({}), new AbortController().signal))
|
||||
expect(error.code).toBe('internal')
|
||||
expect(error.message).toContain('no local document')
|
||||
})
|
||||
|
||||
it('does not prepare or open a settings document after cancellation', async () => {
|
||||
const ctx = await harness({ settings: { documentPath: '/tmp/settings.yaml' } })
|
||||
const opened: string[] = []
|
||||
const api = createApiProxy(ctx, {
|
||||
...DEFAULTS,
|
||||
openTextFile: (path) => {
|
||||
opened.push(path)
|
||||
return Promise.resolve()
|
||||
},
|
||||
})
|
||||
const prepare = vi.spyOn(ctx.settings, 'prepareDocument')
|
||||
const cancelled = new AbortController()
|
||||
cancelled.abort()
|
||||
expect(expectErr(await api.settings.openDocument(request({}), cancelled.signal)).code)
|
||||
.toBe('cancelled')
|
||||
expect(prepare).not.toHaveBeenCalled()
|
||||
|
||||
const pending = Promise.withResolvers<string | undefined>()
|
||||
prepare.mockReturnValueOnce(pending.promise)
|
||||
const duringPrepare = new AbortController()
|
||||
const opening = api.settings.openDocument(request({}), duringPrepare.signal)
|
||||
await vi.waitFor(() => { expect(prepare).toHaveBeenCalledOnce() })
|
||||
duringPrepare.abort()
|
||||
pending.resolve('/tmp/settings.yaml')
|
||||
expect(expectErr(await opening).code).toBe('cancelled')
|
||||
expect(opened).toEqual([])
|
||||
})
|
||||
|
||||
it('serves model-provider and explicitly allowlisted Web namespaces only', async () => {
|
||||
// The settings seam is general: any plugin may register a namespace for
|
||||
// its own configuration. The Web configuration plane remains opt-in, so a
|
||||
|
||||
@@ -96,7 +96,7 @@ function scriptedApi(overrides: {
|
||||
...overrides.goals,
|
||||
},
|
||||
settings: {
|
||||
describe: r => ok(r, { writable: true, namespaces: [] }),
|
||||
describe: r => ok(r, { writable: true, hasDocument: false, namespaces: [] }),
|
||||
openDocument: r => ok(r, { opened: true as const }),
|
||||
update: err,
|
||||
replace: err,
|
||||
@@ -680,7 +680,7 @@ describe('config unary surface', () => {
|
||||
const group = { id: 'deepseek-official', name: 'DeepSeek', models: [{ id: 'deepseek-v4-flash', name: 'Flash' }] }
|
||||
const api = scriptedApi({
|
||||
settings: {
|
||||
describe: record('settings.describe', r => ok(r, { writable: true, namespaces: [view] })),
|
||||
describe: record('settings.describe', r => ok(r, { writable: true, hasDocument: false, namespaces: [view] })),
|
||||
openDocument: record('settings.openDocument', r => ok(r, { opened: true as const })),
|
||||
update: record('settings.update', r => ok(r, view)),
|
||||
replace: record('settings.replace', r => ok(r, view)),
|
||||
@@ -699,7 +699,7 @@ describe('config unary surface', () => {
|
||||
const c = client(api)
|
||||
|
||||
const described = await c.settings.describe({})
|
||||
expect(described.result).toEqual({ ok: true, value: { writable: true, namespaces: [view] } })
|
||||
expect(described.result).toEqual({ ok: true, value: { writable: true, hasDocument: false, namespaces: [view] } })
|
||||
expect((await c.settings.openDocument({})).result).toEqual({ ok: true, value: { opened: true } })
|
||||
const updated = await c.settings.update({ ns: 'llm-deepseek', patch: { baseURL: 'https://next' } })
|
||||
expect(updated.result).toEqual({ ok: true, value: view })
|
||||
|
||||
@@ -220,7 +220,7 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
|
||||
},
|
||||
settings: {
|
||||
async describe(request) {
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { writable: true, namespaces: [] } } }
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { writable: true, hasDocument: false, namespaces: [] } } }
|
||||
},
|
||||
async openDocument(request) {
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { opened: true as const } } }
|
||||
|
||||
@@ -48,6 +48,16 @@ describe('native path opener', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the Windows desktop association for text documents', async () => {
|
||||
const run = vi.fn<PathOpenerRunner>(async () => ({ stdout: '', stderr: '' }))
|
||||
await openNativeTextFile('C:\\work\\settings.yaml', signal(), { platform: 'win32', run })
|
||||
expect(run).toHaveBeenCalledWith(
|
||||
'powershell.exe',
|
||||
['-NoProfile', '-Command', "Invoke-Item -LiteralPath 'C:\\work\\settings.yaml'"],
|
||||
expect.any(AbortSignal),
|
||||
)
|
||||
})
|
||||
|
||||
it('opens with Linux xdg-open', async () => {
|
||||
const run = vi.fn<PathOpenerRunner>(async () => ({ stdout: '', stderr: '' }))
|
||||
await openNativePath('/tmp/a.txt', signal(), { platform: 'linux', run })
|
||||
|
||||
Reference in New Issue
Block a user