test(git): migrate git tests to Effect runner (#27121)

This commit is contained in:
Kit Langton
2026-05-12 14:50:07 -04:00
committed by GitHub
parent ec30ff9120
commit b668af29dd

View File

@@ -1,71 +1,70 @@
import { $ } from "bun" import { $ } from "bun"
import { describe, expect, test } from "bun:test" import { describe, expect } from "bun:test"
import fs from "fs/promises" import fs from "fs/promises"
import path from "path" import path from "path"
import { ManagedRuntime } from "effect" import { Effect } from "effect"
import { Git } from "../../src/git" import { Git } from "../../src/git"
import { tmpdir } from "../fixture/fixture" import { tmpdir } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const weird = process.platform === "win32" ? "space file.txt" : "tab\tfile.txt" const weird = process.platform === "win32" ? "space file.txt" : "tab\tfile.txt"
const it = testEffect(Git.defaultLayer)
async function withGit<T>(body: (rt: ManagedRuntime.ManagedRuntime<Git.Service, never>) => Promise<T>) { const scopedTmpdir = (options?: Parameters<typeof tmpdir>[0]) =>
const rt = ManagedRuntime.make(Git.defaultLayer) Effect.acquireRelease(
try { Effect.promise(() => tmpdir(options)),
return await body(rt) (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
} finally { )
await rt.dispose()
}
}
describe("Git", () => { describe("Git", () => {
test("branch() returns current branch name", async () => { it.live("branch() returns current branch name", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
const tmp = yield* scopedTmpdir({ git: true })
await withGit(async (rt) => { const git = yield* Git.Service
const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) const branch = yield* git.branch(tmp.path)
expect(branch).toBeDefined() expect(branch).toBeDefined()
expect(typeof branch).toBe("string") expect(typeof branch).toBe("string")
}) }),
}) )
test("branch() returns undefined for non-git directories", async () => { it.live("branch() returns undefined for non-git directories", () =>
await using tmp = await tmpdir() Effect.gen(function* () {
const tmp = yield* scopedTmpdir()
await withGit(async (rt) => { const git = yield* Git.Service
const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) const branch = yield* git.branch(tmp.path)
expect(branch).toBeUndefined() expect(branch).toBeUndefined()
}) }),
}) )
test("branch() returns undefined for detached HEAD", async () => { it.live("branch() returns undefined for detached HEAD", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
const hash = (await $`git rev-parse HEAD`.cwd(tmp.path).quiet().text()).trim() const tmp = yield* scopedTmpdir({ git: true })
await $`git checkout --detach ${hash}`.cwd(tmp.path).quiet() const hash = (yield* Effect.promise(() => $`git rev-parse HEAD`.cwd(tmp.path).quiet().text())).trim()
yield* Effect.promise(() => $`git checkout --detach ${hash}`.cwd(tmp.path).quiet())
await withGit(async (rt) => { const git = yield* Git.Service
const branch = await rt.runPromise(Git.Service.use((git) => git.branch(tmp.path))) const branch = yield* git.branch(tmp.path)
expect(branch).toBeUndefined() expect(branch).toBeUndefined()
}) }),
}) )
test("defaultBranch() uses init.defaultBranch when available", async () => { it.live("defaultBranch() uses init.defaultBranch when available", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await $`git branch -M trunk`.cwd(tmp.path).quiet() const tmp = yield* scopedTmpdir({ git: true })
await $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet() yield* Effect.promise(() => $`git branch -M trunk`.cwd(tmp.path).quiet())
yield* Effect.promise(() => $`git config init.defaultBranch trunk`.cwd(tmp.path).quiet())
await withGit(async (rt) => { const git = yield* Git.Service
const branch = await rt.runPromise(Git.Service.use((git) => git.defaultBranch(tmp.path))) const branch = yield* git.defaultBranch(tmp.path)
expect(branch?.name).toBe("trunk") expect(branch?.name).toBe("trunk")
expect(branch?.ref).toBe("trunk") expect(branch?.ref).toBe("trunk")
}) }),
}) )
test("status() handles special filenames", async () => { it.live("status() handles special filenames", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8") const tmp = yield* scopedTmpdir({ git: true })
yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "hello\n", "utf-8"))
await withGit(async (rt) => { const git = yield* Git.Service
const status = await rt.runPromise(Git.Service.use((git) => git.status(tmp.path))) const status = yield* git.status(tmp.path)
expect(status).toEqual( expect(status).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
@@ -74,23 +73,24 @@ describe("Git", () => {
}), }),
]), ]),
) )
}) }),
}) )
test("diff(), stats(), and mergeBase() parse tracked changes", async () => { it.live("diff(), stats(), and mergeBase() parse tracked changes", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await $`git branch -M main`.cwd(tmp.path).quiet() const tmp = yield* scopedTmpdir({ git: true })
await fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8") yield* Effect.promise(() => $`git branch -M main`.cwd(tmp.path).quiet())
await $`git add .`.cwd(tmp.path).quiet() yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8"))
await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet())
await $`git checkout -b feature/test`.cwd(tmp.path).quiet() yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet())
await fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8") yield* Effect.promise(() => $`git checkout -b feature/test`.cwd(tmp.path).quiet())
yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8"))
await withGit(async (rt) => { const git = yield* Git.Service
const [base, diff, stats] = await Promise.all([ const [base, diff, stats] = yield* Effect.all([
rt.runPromise(Git.Service.use((git) => git.mergeBase(tmp.path, "main"))), git.mergeBase(tmp.path, "main"),
rt.runPromise(Git.Service.use((git) => git.diff(tmp.path, "HEAD"))), git.diff(tmp.path, "HEAD"),
rt.runPromise(Git.Service.use((git) => git.stats(tmp.path, "HEAD"))), git.stats(tmp.path, "HEAD"),
]) ])
expect(base).toBeTruthy() expect(base).toBeTruthy()
@@ -111,23 +111,24 @@ describe("Git", () => {
}), }),
]), ]),
) )
}) }),
}) )
test("patch() returns capped native patch output", async () => { it.live("patch() returns capped native patch output", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8") const tmp = yield* scopedTmpdir({ git: true })
await fs.writeFile(path.join(tmp.path, "other.txt"), "old\n", "utf-8") yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "before\n", "utf-8"))
await $`git add .`.cwd(tmp.path).quiet() yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "other.txt"), "old\n", "utf-8"))
await $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet() yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet())
await fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8") yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add file"`.cwd(tmp.path).quiet())
await fs.writeFile(path.join(tmp.path, "other.txt"), "new\n", "utf-8") yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "after\n", "utf-8"))
yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "other.txt"), "new\n", "utf-8"))
await withGit(async (rt) => { const git = yield* Git.Service
const [patch, all, capped] = await Promise.all([ const [patch, all, capped] = yield* Effect.all([
rt.runPromise(Git.Service.use((git) => git.patch(tmp.path, "HEAD", weird, { context: 2_147_483_647 }))), git.patch(tmp.path, "HEAD", weird, { context: 2_147_483_647 }),
rt.runPromise(Git.Service.use((git) => git.patchAll(tmp.path, "HEAD", { context: 2_147_483_647 }))), git.patchAll(tmp.path, "HEAD", { context: 2_147_483_647 }),
rt.runPromise(Git.Service.use((git) => git.patch(tmp.path, "HEAD", weird, { maxOutputBytes: 1 }))), git.patch(tmp.path, "HEAD", weird, { maxOutputBytes: 1 }),
]) ])
expect(patch.truncated).toBe(false) expect(patch.truncated).toBe(false)
@@ -140,17 +141,18 @@ describe("Git", () => {
expect(all.text).toContain("+new") expect(all.text).toContain("+new")
expect(capped.truncated).toBe(true) expect(capped.truncated).toBe(true)
expect(capped.text).toBe("") expect(capped.text).toBe("")
}) }),
}) )
test("patchUntracked() and statUntracked() handle added files", async () => { it.live("patchUntracked() and statUntracked() handle added files", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await fs.writeFile(path.join(tmp.path, weird), "one\ntwo\n", "utf-8") const tmp = yield* scopedTmpdir({ git: true })
yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, weird), "one\ntwo\n", "utf-8"))
await withGit(async (rt) => { const git = yield* Git.Service
const [patch, stat] = await Promise.all([ const [patch, stat] = yield* Effect.all([
rt.runPromise(Git.Service.use((git) => git.patchUntracked(tmp.path, weird, { context: 2_147_483_647 }))), git.patchUntracked(tmp.path, weird, { context: 2_147_483_647 }),
rt.runPromise(Git.Service.use((git) => git.statUntracked(tmp.path, weird))), git.statUntracked(tmp.path, weird),
]) ])
expect(patch.truncated).toBe(false) expect(patch.truncated).toBe(false)
@@ -158,18 +160,19 @@ describe("Git", () => {
expect(patch.text).toContain("+one") expect(patch.text).toContain("+one")
expect(patch.text).toContain("+two") expect(patch.text).toContain("+two")
expect(stat).toEqual(expect.objectContaining({ file: weird, additions: 2, deletions: 0 })) expect(stat).toEqual(expect.objectContaining({ file: weird, additions: 2, deletions: 0 }))
}) }),
}) )
test("show() returns empty text for binary blobs", async () => { it.live("show() returns empty text for binary blobs", () =>
await using tmp = await tmpdir({ git: true }) Effect.gen(function* () {
await fs.writeFile(path.join(tmp.path, "bin.dat"), new Uint8Array([0, 1, 2, 3])) const tmp = yield* scopedTmpdir({ git: true })
await $`git add .`.cwd(tmp.path).quiet() yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "bin.dat"), new Uint8Array([0, 1, 2, 3])))
await $`git commit --no-gpg-sign -m "add binary"`.cwd(tmp.path).quiet() yield* Effect.promise(() => $`git add .`.cwd(tmp.path).quiet())
yield* Effect.promise(() => $`git commit --no-gpg-sign -m "add binary"`.cwd(tmp.path).quiet())
await withGit(async (rt) => { const git = yield* Git.Service
const text = await rt.runPromise(Git.Service.use((git) => git.show(tmp.path, "HEAD", "bin.dat"))) const text = yield* git.show(tmp.path, "HEAD", "bin.dat")
expect(text).toBe("") expect(text).toBe("")
}) }),
}) )
}) })