snapshot-path-bug.test.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { test, expect } from "bun:test"
  2. import { $ } from "bun"
  3. import path from "path"
  4. import { Snapshot } from "../../src/snapshot"
  5. import { Instance } from "../../src/project/instance"
  6. async function bootstrap() {
  7. const dir = await $`mktemp -d`.text().then((t) => t.trim())
  8. // Randomize file contents to ensure unique git repos
  9. const unique = Math.random().toString(36).slice(2)
  10. const aContent = `A${unique}`
  11. const bContent = `B${unique}`
  12. await Bun.write(`${dir}/a.txt`, aContent)
  13. await Bun.write(`${dir}/b.txt`, bContent)
  14. await $`git init`.cwd(dir).quiet()
  15. await $`git add .`.cwd(dir).quiet()
  16. await $`git commit -m init`.cwd(dir).quiet()
  17. return {
  18. [Symbol.asyncDispose]: async () => {
  19. await $`rm -rf ${dir}`.quiet()
  20. },
  21. dir,
  22. aContent,
  23. bContent,
  24. }
  25. }
  26. test("file path bug - git returns paths with worktree prefix", async () => {
  27. await using tmp = await bootstrap()
  28. await Instance.provide(tmp.dir, async () => {
  29. const before = await Snapshot.track()
  30. expect(before).toBeTruthy()
  31. // Create a file in subdirectory
  32. await $`mkdir -p ${tmp.dir}/sub`.quiet()
  33. await Bun.write(`${tmp.dir}/sub/file.txt`, "SUB")
  34. // Get the patch - this will demonstrate the path bug
  35. const patch = await Snapshot.patch(before!)
  36. // Log what we get to see the actual paths
  37. console.log("Worktree path:", Instance.worktree)
  38. console.log("Patch files:", patch.files)
  39. // The bug: if git returns paths that already include the worktree directory,
  40. // path.join(Instance.worktree, x) will create double paths
  41. // For example: if git returns "tmpDir/sub/file.txt" and worktree is "tmpDir",
  42. // we get "tmpDir/tmpDir/sub/file.txt" which is wrong
  43. // Check if any paths are duplicated
  44. const hasDoublePaths = patch.files.some((filePath) => {
  45. const worktreeParts = Instance.worktree.split("/").filter(Boolean)
  46. const fileParts = filePath.split("/").filter(Boolean)
  47. // Check if worktree appears twice at the start
  48. if (worktreeParts.length > 0 && fileParts.length >= worktreeParts.length * 2) {
  49. const firstWorktree = fileParts.slice(0, worktreeParts.length).join("/")
  50. const secondWorktree = fileParts.slice(worktreeParts.length, worktreeParts.length * 2).join("/")
  51. return firstWorktree === secondWorktree
  52. }
  53. return false
  54. })
  55. expect(hasDoublePaths).toBe(false) // This test will fail if the bug exists
  56. })
  57. })
  58. test("file path bug - manual demonstration", async () => {
  59. await using tmp = await bootstrap()
  60. await Instance.provide(tmp.dir, async () => {
  61. const before = await Snapshot.track()
  62. expect(before).toBeTruthy()
  63. // Create a file
  64. await Bun.write(`${tmp.dir}/test.txt`, "TEST")
  65. // Simulate what happens in the patch function
  66. // Mock git diff returning a path that already includes worktree
  67. const mockGitOutput = `${Instance.worktree}/test.txt\n`
  68. // This is what the current code does:
  69. const files = mockGitOutput
  70. .trim()
  71. .split("\n")
  72. .map((x) => x.trim())
  73. .filter(Boolean)
  74. .map((x) => path.join(Instance.worktree, x)) // This is the bug!
  75. console.log("Mock git output:", mockGitOutput)
  76. console.log("Result after path.join:", files)
  77. // This will show the double path: /tmp/dir/tmp/dir/test.txt
  78. expect(files[0]).toBe(`${Instance.worktree}/test.txt`) // This should pass but won't due to the bug
  79. })
  80. })