test(api-gateway): cover client mount rollback

This commit is contained in:
imccyu
2026-08-07 11:22:52 +08:00
parent 0ad5885015
commit 5ea6311949

View File

@@ -258,6 +258,13 @@ describe('Client TypeRT API', () => {
package: '@fixture/service-method-conflict', package: '@fixture/service-method-conflict',
descriptors: [{ ...context, id: '@fixture/goals#goals/remove', method: 'remove' }], descriptors: [{ ...context, id: '@fixture/goals#goals/remove', method: 'remove' }],
})).toThrow('conflicts with its namespace service') })).toThrow('conflicts with its namespace service')
const scopedService = ctx.get('goals') as unknown as object
Object.defineProperty(scopedService, 'custom', { configurable: true, value: () => undefined })
expect(() => ctx.api.mount({
package: '@fixture/service-own-property-conflict',
descriptors: [{ ...direct, id: '@fixture/goals#goals/custom', method: 'custom' }],
})).toThrow('conflicts with its namespace service')
Reflect.deleteProperty(scopedService, 'custom')
await disposeScoped() await disposeScoped()
expect(() => ctx.api.mount({ expect(() => ctx.api.mount({
@@ -281,20 +288,37 @@ describe('Client TypeRT API', () => {
it('rolls back direct projection when scoped installation fails', async () => { it('rolls back direct projection when scoped installation fails', async () => {
const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>()) const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>())
const descriptor: InvocationDescriptor = { const disposeScoped = ctx.api.mount({
...directDescriptor(), package: '@fixture/scoped-base',
id: '@fixture/goals#fresh/remove', descriptors: [contextDescriptor()],
namespace: 'fresh', })
method: 'remove', const defineProperty = Object.defineProperty
let createDefinitions = 0
const definePropertySpy = vi.spyOn(Object, 'defineProperty').mockImplementation((target, key, attributes) => {
// The direct projection defines `create` first; fail the following scoped projection.
if (key === 'create' && ++createDefinitions === 2) throw new Error('simulated scoped installation failure')
return defineProperty(target, key, attributes)
})
try {
expect(() => ctx.api.mount({
package: '@fixture/failing-install',
descriptors: [directDescriptor()],
})).toThrow('simulated scoped installation failure')
} finally {
definePropertySpy.mockRestore()
} }
for (const packageName of ['@fixture/first-attempt', '@fixture/second-attempt']) { expect((ctx.api as unknown as Record<string, unknown>).goals).toBeUndefined()
expect(() => ctx.api.mount({ package: packageName, descriptors: [descriptor] })) expect(ctx.get('goals') !== undefined).toBe(true)
.toThrow('conflicts with its namespace service') expect(ctx.typert.remotes.list()).toHaveLength(1)
expect((ctx.api as unknown as Record<string, unknown>).fresh).toBeUndefined()
expect(ctx.get('fresh')).toBeUndefined() const disposeRetry = ctx.api.mount({
expect(ctx.typert.remotes.list()).toEqual([]) package: '@fixture/retry',
} descriptors: [directDescriptor()],
})
await disposeRetry()
await disposeScoped()
}) })
it('rejects weak parameter and Context codecs plus malformed scope projections', async () => { it('rejects weak parameter and Context codecs plus malformed scope projections', async () => {