git.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { $ } from "bun"
  2. import { describe, expect, test } from "bun:test"
  3. import fs from "fs/promises"
  4. import path from "path"
  5. import { ManagedRuntime } from "effect"
  6. import { Git } from "../../src/git"
  7. import { tmpdir } from "../fixture/fixture"
  8. const weird = process.platform === "win32" ? "space file.txt" : "tab\tfile.txt"
  9. async function withGit<T>(body: (rt: ManagedRuntime.ManagedRuntime<Git.Service, never>) => Promise<T>) {
  10. const rt = ManagedRuntime.make(Git.defaultLayer)
  11. try {
  12. return await body(rt)
  13. } finally {
  14. await rt.dispose()
  15. }
  16. }
  17. describe("Git", () => {
  18. test("branch() returns current branch name", async () => {
  19. await using tmp = await tmpdir({ git: true })
  20. await withGit(async (rt) => {
  21. const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path)))
  22. expect(branch).toBeDefined()
  23. expect(typeof branch).toBe("string")
  24. })
  25. })
  26. test("branch() returns undefined for non-git directories", async () => {
  27. await using tmp = await tmpdir()
  28. await withGit(async (rt) => {
  29. const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path)))
  30. expect(branch).toBeUndefined()
  31. })
  32. })
  33. test("branch() returns undefined for detached HEAD", async () => {
  34. await using tmp = await tmpdir({ git: true })
  35. const hash = (await $`git rev-parse HEAD`.cwd(tmp.path).quiet().text()).trim()
  36. await $`git checkout --detach ${hash}`.cwd(tmp.path).quiet()
  37. await withGit(async (rt) => {
  38. const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path)))
  39. expect(branch).toBeUndefined()
  40. })
  41. })
  42. test("defaultBranch() uses init.defaultBranch when available", async () => {
  43. await using tmp = await tmpdir({ git: true })
  44. await $`git branch -M trunk`.cwd(tmp.path).quiet()
  45. await $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet()
  46. await withGit(async (rt) => {
  47. const branch = await rt.runPromise(Git.Service.use((git) => git.defaultBranch(tmp.path)))
  48. expect(branch?.name).toBe("trunk")
  49. expect(branch?.ref).toBe("trunk")
  50. })
  51. })
  52. test("status() handles special filenames", async () => {
  53. await using tmp = await tmpdir({ git: true })
  54. await fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8")
  55. await withGit(async (rt) => {
  56. const status = await rt.runPromise(Git.Service.use((git) => git.status(tmp.path)))
  57. expect(status).toEqual(
  58. expect.arrayContaining([
  59. expect.objectContaining({
  60. file: weird,
  61. status: "added",
  62. }),
  63. ]),
  64. )
  65. })
  66. })
  67. test("diff(), stats(), and mergeBase() parse tracked changes", async () => {
  68. await using tmp = await tmpdir({ git: true })
  69. await $`git branch -M main`.cwd(tmp.path).quiet()
  70. await fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8")
  71. await $`git add .`.cwd(tmp.path).quiet()
  72. await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet()
  73. await $`git checkout -b feature/test`.cwd(tmp.path).quiet()
  74. await fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8")
  75. await withGit(async (rt) => {
  76. const [base, diff, stats] = await Promise.all([
  77. rt.runPromise(Git.Service.use((git) => git.mergeBase(tmp.path, "main"))),
  78. rt.runPromise(Git.Service.use((git) => git.diff(tmp.path, "HEAD"))),
  79. rt.runPromise(Git.Service.use((git) => git.stats(tmp.path, "HEAD"))),
  80. ])
  81. expect(base).toBeTruthy()
  82. expect(diff).toEqual(
  83. expect.arrayContaining([
  84. expect.objectContaining({
  85. file: weird,
  86. status: "modified",
  87. }),
  88. ]),
  89. )
  90. expect(stats).toEqual(
  91. expect.arrayContaining([
  92. expect.objectContaining({
  93. file: weird,
  94. additions: 1,
  95. deletions: 1,
  96. }),
  97. ]),
  98. )
  99. })
  100. })
  101. test("show() returns empty text for binary blobs", async () => {
  102. await using tmp = await tmpdir({ git: true })
  103. await fs.writeFile(path.join(tmp.path, "bin.dat"), new Uint8Array([0, 1, 2, 3]))
  104. await $`git add .`.cwd(tmp.path).quiet()
  105. await $`git commit --no-gpg-sign -m "add binary"`.cwd(tmp.path).quiet()
  106. await withGit(async (rt) => {
  107. const text = await rt.runPromise(Git.Service.use((git) => git.show(tmp.path, "HEAD", "bin.dat")))
  108. expect(text).toBe("")
  109. })
  110. })
  111. })