apply.client.spec.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. /**
  2. * The plugin body: one `file` provider registered into `ctx.resources` for the
  3. * fiber's lifetime, reading through `ctx.remote` in the Session each address names.
  4. */
  5. import { Context } from '@deepseek-ai/cordis'
  6. import type { ResourceProvider } from '@deepseek-ai/dsh-client-resources/client'
  7. import { sessionFileAddress } from '@deepseek-ai/dsh-util-workspace-path'
  8. import { describe, expect, it, onTestFinished } from 'vitest'
  9. import { apply, inject } from '../src/client/index.ts'
  10. import { FakeRemote, settle } from './fake-remote.client.ts'
  11. describe('workspace-files client apply', () => {
  12. it('registers the file provider over ctx.remote, and unregisters it with the fiber', async () => {
  13. const ctx = new Context()
  14. const remote = new FakeRemote()
  15. const controller = new AbortController()
  16. const pulls: Array<Promise<unknown>> = []
  17. ctx.provide('remote', remote as never)
  18. ctx.provide('remote.workspaceFiles', remote.workspaceFiles as never)
  19. const registered: Array<ResourceProvider<'file'>> = []
  20. let released = 0
  21. ctx.provide('resources', {
  22. register: (provider: ResourceProvider<'file'>) => {
  23. registered.push(provider)
  24. return () => {
  25. released++
  26. controller.abort()
  27. for (const request of remote.stats) {
  28. request.resolve({ ok: true, value: { absolutePath: '/host/late-stat', version: 'v0' } })
  29. }
  30. }
  31. },
  32. } as never)
  33. const fiber = ctx.plugin({ inject: [...inject], apply })
  34. onTestFinished(async () => {
  35. await fiber.dispose()
  36. await Promise.all(pulls)
  37. })
  38. await fiber.await()
  39. expect(registered.map(provider => provider.protocol)).toEqual(['file'])
  40. const signal = controller.signal
  41. // A session address reaches the Host with its relative or absolute path.
  42. pulls.push(registered[0]!.open(sessionFileAddress('s1', 'a.txt'), { signal })[Symbol.asyncIterator]().next())
  43. pulls.push(registered[0]!.open(sessionFileAddress('s1', '/etc/hosts'), { signal })[Symbol.asyncIterator]().next())
  44. await settle()
  45. expect(remote.stats.map(pending => [pending.sessionId, pending.path])).toEqual([['s1', 'a.txt'], ['s1', '/etc/hosts']])
  46. expect(remote.opened).toHaveLength(1)
  47. await fiber.dispose()
  48. await Promise.all(pulls)
  49. expect(released).toBe(1)
  50. expect(remote.opened[0]!.source.aborted).toBe(true)
  51. })
  52. })