test: cover gateway rollback and theme fallback branches

This commit is contained in:
imccyu
2026-08-20 16:13:05 +08:00
parent d4fd03ae53
commit 3ca4997cd2
2 changed files with 79 additions and 0 deletions
@@ -458,6 +458,76 @@ describe('Client Typert API', () => {
await disposeContext()
})
it('unwinds an already-installed namespace when a later namespace fails to install', async () => {
const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>())
const { scope: _scope, ...probe } = directDescriptor()
const vault: InvocationDescriptor = {
...probe,
id: '@fixture/vault#vault/seal',
service: 'vault',
namespace: 'vault',
method: 'seal',
}
const defineProperty = Object.defineProperty
const spy = vi.spyOn(Object, 'defineProperty').mockImplementation((target, key, attributes) => {
if (key === 'seal') throw new Error('fixture later-namespace failure')
return defineProperty(target, key, attributes)
})
try {
await expect(ctx.remote.$mount({ package: '@fixture/two-namespaces', descriptors: [probe, vault] }))
.rejects.toThrow('fixture later-namespace failure')
} finally {
spy.mockRestore()
}
expect((ctx.remote as unknown as Record<string, unknown>).probe).toBeUndefined()
expect((ctx.remote as unknown as Record<string, unknown>).vault).toBeUndefined()
await vi.waitFor(() => { expect(ctx.typert.remotes.list()).toEqual([]) })
})
it('rolls back an earlier scoped projection when a later descriptor fails to install', async () => {
const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>())
const { scope: _scope, ...direct } = directDescriptor()
const failing: InvocationDescriptor = {
...direct,
id: '@fixture/probe#probe/archive',
method: 'archive',
}
const defineProperty = Object.defineProperty
const spy = vi.spyOn(Object, 'defineProperty').mockImplementation((target, key, attributes) => {
if (key === 'archive') throw new Error('fixture trailing failure')
return defineProperty(target, key, attributes)
})
try {
await expect(ctx.remote.$mount({
package: '@fixture/scoped-then-failing',
descriptors: [contextDescriptor(), failing],
})).rejects.toThrow('fixture trailing failure')
} finally {
spy.mockRestore()
}
expect((ctx.remote as unknown as Record<string, unknown>).probe).toBeUndefined()
await vi.waitFor(() => { expect(ctx.typert.remotes.list()).toEqual([]) })
})
it('keeps a namespace another contribution still populates when a group leaves', async () => {
const call = vi.fn<ConnectionHandle['rpc']['call']>()
.mockResolvedValue({ ok: true, value: { renamed: true } })
const ctx = await bench(call)
const { scope: _scope, ...direct } = directDescriptor()
const disposeDirect = await ctx.remote.$mount({ package: '@fixture/direct-owner', descriptors: [direct] })
const disposeScoped = await ctx.remote.$mount({ package: '@fixture/scoped-owner', descriptors: [contextDescriptor()] })
await disposeDirect()
// The namespace survives its first group: the second contribution still owns methods on it.
const surviving = ctx.get('remote.probe') as unknown as Record<string, unknown> | undefined
expect(surviving).toBeDefined()
expect(surviving?.create).toBeUndefined()
await disposeScoped()
expect(ctx.get('remote.probe')).toBeUndefined()
})
it('rejects weak parameter and Context codecs plus malformed scope projections', async () => {
const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>())
const direct = directDescriptor()
@@ -62,4 +62,13 @@ describe('ui-theme host', () => {
await ctx.plugin({ apply }).await()
expect(scriptText(collect(ctx)[0])).toContain('const preference = "system"')
})
it('falls back to the schema default while the theme namespace holds no section', async () => {
// A settings provider whose namespace read comes back empty (registration
// still pending or a provider without schema defaults).
const ctx = new Context()
ctx.provide('settings', { register: () => () => {}, get: () => undefined } as never)
await ctx.plugin({ apply }).await()
expect(scriptText(collect(ctx)[0])).toContain('const preference = "system"')
})
})