Browse Source

test(api-gateway): cover client mount rollback

imccyu 2 months ago
parent
commit
5ea6311949
1 changed files with 36 additions and 12 deletions
  1. 36 12
      packages/host/api-gateway/tests/client.spec.ts

+ 36 - 12
packages/host/api-gateway/tests/client.spec.ts

@@ -258,6 +258,13 @@ describe('Client TypeRT API', () => {
       package: '@fixture/service-method-conflict',
       descriptors: [{ ...context, id: '@fixture/goals#goals/remove', method: 'remove' }],
     })).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()
 
     expect(() => ctx.api.mount({
@@ -281,20 +288,37 @@ describe('Client TypeRT API', () => {
 
   it('rolls back direct projection when scoped installation fails', async () => {
     const ctx = await bench(vi.fn<ConnectionHandle['rpc']['call']>())
-    const descriptor: InvocationDescriptor = {
-      ...directDescriptor(),
-      id: '@fixture/goals#fresh/remove',
-      namespace: 'fresh',
-      method: 'remove',
-    }
+    const disposeScoped = ctx.api.mount({
+      package: '@fixture/scoped-base',
+      descriptors: [contextDescriptor()],
+    })
+    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)
+    })
 
-    for (const packageName of ['@fixture/first-attempt', '@fixture/second-attempt']) {
-      expect(() => ctx.api.mount({ package: packageName, descriptors: [descriptor] }))
-        .toThrow('conflicts with its namespace service')
-      expect((ctx.api as unknown as Record<string, unknown>).fresh).toBeUndefined()
-      expect(ctx.get('fresh')).toBeUndefined()
-      expect(ctx.typert.remotes.list()).toEqual([])
+    try {
+      expect(() => ctx.api.mount({
+        package: '@fixture/failing-install',
+        descriptors: [directDescriptor()],
+      })).toThrow('simulated scoped installation failure')
+    } finally {
+      definePropertySpy.mockRestore()
     }
+
+    expect((ctx.api as unknown as Record<string, unknown>).goals).toBeUndefined()
+    expect(ctx.get('goals') !== undefined).toBe(true)
+    expect(ctx.typert.remotes.list()).toHaveLength(1)
+
+    const disposeRetry = ctx.api.mount({
+      package: '@fixture/retry',
+      descriptors: [directDescriptor()],
+    })
+    await disposeRetry()
+    await disposeScoped()
   })
 
   it('rejects weak parameter and Context codecs plus malformed scope projections', async () => {