snapshot-shell-path.spec.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. import { Context } from '@deepseek-ai/cordis'
  2. import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
  3. import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
  4. import { mkdtemp, readFile, rm } from 'node:fs/promises'
  5. import { tmpdir } from 'node:os'
  6. import { join } from 'node:path'
  7. import { expect, it } from 'vitest'
  8. import * as adapter from './snapshot-shell-path.ts'
  9. it.skipIf(process.platform === 'win32')('translates only the exact fixture command and preserves real shell failures', async () => {
  10. const root = await mkdtemp(join(tmpdir(), 'snapshot-shell-'))
  11. const ctx = new Context()
  12. const disposers: (() => Promise<void>)[] = []
  13. try {
  14. const subprocess = ctx.plugin(LocalSubprocessRuntime)
  15. disposers.push(() => subprocess.dispose())
  16. await subprocess
  17. const bash = ctx.plugin(LocalBashExecutor)
  18. disposers.push(() => bash.dispose())
  19. await bash
  20. const command = "printf 'actual bytes' > /fixture/recorded.txt"
  21. const livePath = join(root, "space and 'quote.txt")
  22. const fork = ctx.plugin(adapter, { command, recordedPath: '/fixture/recorded.txt', livePath })
  23. disposers.push(() => fork.dispose())
  24. await fork
  25. const outcome = await ctx.shell.run(ctx.shell.resolve({ command }))
  26. expect(outcome.timedOut).toBe(false)
  27. expect(outcome.exitCode).toBe(0)
  28. expect(await readFile(livePath, 'utf8')).toBe('actual bytes')
  29. const untouched = await ctx.shell.run(ctx.shell.resolve({ command: "printf '/fixture/recorded.txt'; exit 7" }))
  30. expect(untouched.exitCode).toBe(7)
  31. expect(untouched.stdout.text).toBe('/fixture/recorded.txt')
  32. await fork.dispose()
  33. await rm(livePath)
  34. const restored = await ctx.shell.run(ctx.shell.resolve({ command }))
  35. expect(restored.exitCode).not.toBe(0)
  36. await expect(readFile(livePath)).rejects.toMatchObject({ code: 'ENOENT' })
  37. } finally {
  38. for (const dispose of disposers.reverse()) await dispose()
  39. await rm(root, { recursive: true, force: true })
  40. }
  41. })