diff --git a/packages/client/test-runtime/src/workspaces.ts b/packages/client/test-runtime/src/workspaces.ts index 3505de0853..6c1a9d0aad 100644 --- a/packages/client/test-runtime/src/workspaces.ts +++ b/packages/client/test-runtime/src/workspaces.ts @@ -115,10 +115,13 @@ export class TestWorkspaces implements IWorkspaces { * @param path - absolute directory to list; absent lists the home level. * @returns the level's listing. */ - async listDirectory(path?: string, _signal?: AbortSignal): Promise { - this.calls.push({ method: 'listDirectory', args: [path] }) + async listDirectory(path?: string, signal?: AbortSignal): Promise { + // The signal is recorded and forwarded like the production face passes + // it to the wire, so cancellation integration tests can observe or + // reject on a superseded scan. + this.calls.push({ method: 'listDirectory', args: [path, signal] }) const stub = this.stubs.get('listDirectory') - if (stub !== undefined) return await (stub(path) as Promise) + if (stub !== undefined) return await (stub(path, signal) as Promise) // The chain runs root-to-target inclusive, per the DirectoryListing // contract — a bare root crumb would mislabel the level in browsers // driven by this double. diff --git a/packages/client/test-runtime/tests/runtime.spec.tsx b/packages/client/test-runtime/tests/runtime.spec.tsx index a6f22a85fe..b170f69ba2 100644 --- a/packages/client/test-runtime/tests/runtime.spec.tsx +++ b/packages/client/test-runtime/tests/runtime.spec.tsx @@ -329,16 +329,22 @@ describe('workspaces', () => { await expect(runtime.workspaces.listDirectory()).resolves.toMatchObject({ path: '/home/test', entries: [] }) await expect(runtime.workspaces.listDirectory('/home/test')).resolves.toMatchObject({ path: '/home/test' }) await expect(runtime.workspaces.createDirectory('/home/test', 'fresh')).resolves.toBe('/home/test/fresh') + // The recorded signal seat mirrors the production face (undefined here; + // cancellation tests pass and observe a real one). expect(runtime.workspaces.calls).toEqual([ - { method: 'listDirectory', args: [undefined] }, - { method: 'listDirectory', args: ['/home/test'] }, + { method: 'listDirectory', args: [undefined, undefined] }, + { method: 'listDirectory', args: ['/home/test', undefined] }, { method: 'createDirectory', args: ['/home/test', 'fresh'] }, ]) // Stubs replace the defaults like every sibling method. const listing = { path: '/x', home: '/x', crumbs: [], entries: [] } - runtime.workspaces.stub('listDirectory', vi.fn(() => Promise.resolve(listing as never))) + const listStub = vi.fn(() => Promise.resolve(listing as never)) + runtime.workspaces.stub('listDirectory', listStub) runtime.workspaces.stub('createDirectory', vi.fn(() => Promise.resolve('/x/made' as never))) - await expect(runtime.workspaces.listDirectory('/x')).resolves.toBe(listing) + const scan = new AbortController() + await expect(runtime.workspaces.listDirectory('/x', scan.signal)).resolves.toBe(listing) + // The stub receives the signal too, like the production face gives the wire. + expect(listStub).toHaveBeenLastCalledWith('/x', scan.signal) await expect(runtime.workspaces.createDirectory('/x', 'made')).resolves.toBe('/x/made') await runtime.dispose() })