session-query-spill-command.spec.ts 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { Context } from '@deepseek-ai/cordis'
  2. import { LocalSpillStore } from '@deepseek-ai/dsh-spill-local'
  3. import { LocalFileSystem } from '@deepseek-ai/dsh-fs-local'
  4. import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
  5. import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
  6. import { SessionId } from '@deepseek-ai/dsh-session'
  7. import { ToolCallId } from '@deepseek-ai/dsh-llm'
  8. import { mkdtemp, rm } from 'node:fs/promises'
  9. import { tmpdir } from 'node:os'
  10. import { join, resolve } from 'node:path'
  11. import { expect, it } from 'vitest'
  12. import * as locators from './snapshot-spill-locators.ts'
  13. import * as commands from '../snapshots/session/session-query-spill/resolve-spill-command.mjs'
  14. it.skipIf(process.platform === 'win32')('resolves the exact spill verifier, retains real failures, and restores the shell', async () => {
  15. const root = await mkdtemp(join(tmpdir(), "query-spill-'quoted-"))
  16. const ctx = new Context()
  17. const disposers: (() => Promise<void>)[] = []
  18. try {
  19. for (const fiber of [
  20. ctx.plugin(LocalSpillStore, { root, cleanupPeriodDays: 0 }),
  21. ctx.plugin(LocalFileSystem, { cwd: root }),
  22. ctx.plugin(LocalSubprocessRuntime),
  23. ctx.plugin(LocalBashExecutor),
  24. ]) {
  25. disposers.push(() => fiber.dispose())
  26. await fiber
  27. }
  28. const logical = resolve('/tmp/dsh-acp-snap-query-verifier')
  29. const mapping = ctx.plugin(locators, { root, locatorRoot: logical })
  30. disposers.push(() => mapping.dispose())
  31. await mapping
  32. const saved = await ctx.spillStore.saveText({
  33. owner: { sessionId: SessionId('query-verifier') },
  34. source: { kind: 'tool', toolName: 'session_event_read', callId: ToolCallId('query'), label: 'result' },
  35. suggestedName: 'session_event_read.txt', content: 'request/header session_event_search',
  36. })
  37. const adapter = ctx.plugin(commands)
  38. disposers.push(() => adapter.dispose())
  39. await adapter
  40. const command = 'file="' + saved.locator + '"' + "; grep -Fq request/header \"$file\" && grep -Fq session_event_search \"$file\" && printf 'SPILL_CANONICAL_OK\\n'"
  41. const spec = ctx.shell.resolve({ command })
  42. const output = await ctx.shell.run(spec)
  43. expect(output.timedOut).toBe(false)
  44. expect(output.exitCode).toBe(0)
  45. expect(output.stdout.text).toBe('SPILL_CANONICAL_OK\n')
  46. expect(spec.command).toBe(command)
  47. const other = await ctx.shell.run(ctx.shell.resolve({ command: 'printf ordinary; exit 7' }))
  48. expect(other.exitCode).toBe(7)
  49. expect(other.stdout.text).toBe('ordinary')
  50. const unmatched = await ctx.shell.run(ctx.shell.resolve({ command: command + '; exit 9' }))
  51. expect(unmatched.exitCode).toBe(9)
  52. expect(unmatched.stderr.text).toContain('No such file')
  53. const physical = ctx.fs.processPath(await ctx.fs.resolve(saved.locator))
  54. await rm(physical)
  55. const missing = await ctx.shell.run(spec)
  56. expect(missing.exitCode).not.toBe(0)
  57. expect(missing.stdout.text).toBe('')
  58. await adapter.dispose()
  59. const restored = await ctx.shell.run(spec)
  60. expect(restored.exitCode).not.toBe(0)
  61. expect(restored.stderr.text).toContain(saved.locator)
  62. } finally {
  63. for (const dispose of disposers.reverse()) await dispose()
  64. await rm(root, { recursive: true, force: true })
  65. }
  66. })