rpc.client.spec.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. /**
  2. * The address-to-read translation: a `dsh-resource://file/session/<id>/<path>`
  3. * address names the session the read runs under and the workspace-relative path
  4. * it hands the Host; absolute paths also travel inside the Session address.
  5. * An address without a Session fails loud.
  6. */
  7. import { describe, expect, it, vi } from 'vitest'
  8. import { sessionFileAddress } from '@deepseek-ai/dsh-util-workspace-path'
  9. import { createReadPage, documentFileBytes, hostFileOf } from '../src/client/rpc.ts'
  10. import type { ReadWorkspaceFilePage, WorkspaceFilesReadRemote } from '../src/client/index.ts'
  11. import { ADDRESS, FILE, PATH, SESSION, page } from './fixtures.client.ts'
  12. describe('hostFileOf', () => {
  13. it('reads the session and the decoded relative path out of a session file address', () => {
  14. expect(hostFileOf(ADDRESS)).toEqual(FILE)
  15. expect(hostFileOf('dsh-resource://file/session/s%2F1/work/a%20b%23c.md')).toEqual({ sessionId: 's/1', path: 'work/a b#c.md' })
  16. })
  17. it.each(['/etc/hosts', 'C:/w/a b.md', '//host/share/a b.md'])('preserves the Session address\'s absolute path %s for the Host', (path) => {
  18. expect(hostFileOf(sessionFileAddress(SESSION, path))).toEqual({ sessionId: SESSION, path })
  19. })
  20. it('throws for an address that is not a Session file address', () => {
  21. for (const address of [
  22. 'dsh-resource://file/absolute/etc/hosts', 'dsh-resource://file/absolute/C:/w/a.md',
  23. 'dsh-resource://file/shared/team/notes.md', 'dsh-resource://file/session', 'file:///work/notes.md', 'sidebar://guide',
  24. ]) {
  25. expect(() => hostFileOf(address)).toThrow('not a session file address')
  26. }
  27. })
  28. })
  29. describe('createReadPage', () => {
  30. it('binds the paged read to the Remote with the offset as the only range', async () => {
  31. const read = vi.fn<WorkspaceFilesReadRemote['workspaceFiles']['read']>(() => Promise.resolve(page(4, ['d'], true)))
  32. const signal = new AbortController().signal
  33. const readPage: ReadWorkspaceFilePage = createReadPage({ workspaceFiles: { read } })
  34. await expect(readPage(SESSION, PATH, 4, signal)).resolves.toEqual(page(4, ['d'], true))
  35. expect(read).toHaveBeenCalledWith(SESSION, PATH, { offset: 4 }, signal)
  36. })
  37. })
  38. describe('documentFileBytes', () => {
  39. it.each(['', 'AAH/'])('decodes complete Remote bytes without changing metadata (%j)', (data) => {
  40. const file = { absolutePath: '/workspace/a.bin', version: 'v1', bytes: 3, offset: 0, data, eof: true }
  41. const result = documentFileBytes(file)
  42. expect(result).toEqual({ ...file, data: data === '' ? new Uint8Array() : new Uint8Array([0, 1, 255]) })
  43. expect(result.data.buffer).toBeInstanceOf(ArrayBuffer)
  44. expect(file.data).toBe(data)
  45. })
  46. it('rejects malformed wire base64', () => {
  47. expect(() => documentFileBytes({ absolutePath: '/workspace/a.bin', version: 'v1', offset: 0, data: '!!!', eof: true })).toThrow()
  48. })
  49. })