| 1 | // @effect-diagnostics nodeBuiltinImport:off - builds real worktree layouts on disk. |
| 2 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 3 | import { assert, describe, it } from "@effect/vitest"; |
| 4 | import * as NodeFS from "node:fs"; |
| 5 | import * as NodeOS from "node:os"; |
| 6 | import * as NodePath from "node:path"; |
| 7 | import * as Effect from "effect/Effect"; |
| 8 | |
| 9 | import { resolveGitWorktreePath, resolveWorktreeT3Home } from "./devHome.ts"; |
| 10 | |
| 11 | const makeRepo = ( |
| 12 | kind: |
| 13 | | "worktree" |
| 14 | | "checkout" |
| 15 | | "bare" |
| 16 | | "submodule" |
| 17 | | "unreadable-git-file" |
| 18 | | "bare-repo-worktree" |
| 19 | | "custom-common-dir-worktree", |
| 20 | ) => |
| 21 | Effect.acquireRelease( |
| 22 | Effect.sync(() => { |
| 23 | const root = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-devhome-")); |
| 24 | if (kind === "worktree") { |
| 25 | NodeFS.writeFileSync(NodePath.join(root, ".git"), "gitdir: /elsewhere/.git/worktrees/x\n"); |
| 26 | } else if (kind === "bare-repo-worktree") { |
| 27 | // `git worktree add` from a bare repo: the common dir is `<name>.git`. |
| 28 | NodeFS.writeFileSync(NodePath.join(root, ".git"), "gitdir: /srv/myrepo.git/worktrees/x\n"); |
| 29 | } else if (kind === "custom-common-dir-worktree") { |
| 30 | // $GIT_COMMON_DIR need not be named `.git` at all. |
| 31 | NodeFS.writeFileSync(NodePath.join(root, ".git"), "gitdir: /srv/store/worktrees/x\n"); |
| 32 | } else if (kind === "submodule") { |
| 33 | NodeFS.writeFileSync(NodePath.join(root, ".git"), "gitdir: ../.git/modules/sub\n"); |
| 34 | } else if (kind === "unreadable-git-file") { |
| 35 | NodeFS.writeFileSync(NodePath.join(root, ".git"), "not a gitdir pointer\n"); |
| 36 | } else if (kind === "checkout") { |
| 37 | NodeFS.mkdirSync(NodePath.join(root, ".git")); |
| 38 | } |
| 39 | const nested = NodePath.join(root, "apps", "web", "src"); |
| 40 | NodeFS.mkdirSync(nested, { recursive: true }); |
| 41 | return { root, nested }; |
| 42 | }), |
| 43 | ({ root }) => Effect.sync(() => NodeFS.rmSync(root, { recursive: true, force: true })), |
| 44 | ); |
| 45 | |
| 46 | describe("resolveGitWorktreePath", () => { |
| 47 | it.effect("finds a worktree root from a nested directory", () => |
| 48 | Effect.gen(function* () { |
| 49 | const { root, nested } = yield* makeRepo("worktree"); |
| 50 | assert.equal(yield* resolveGitWorktreePath(nested), NodePath.resolve(root)); |
| 51 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 52 | ); |
| 53 | |
| 54 | it.effect("reports a main checkout as not a linked worktree", () => |
| 55 | Effect.gen(function* () { |
| 56 | const { nested } = yield* makeRepo("checkout"); |
| 57 | assert.equal(yield* resolveGitWorktreePath(nested), undefined); |
| 58 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 59 | ); |
| 60 | |
| 61 | it.effect("reports a directory outside a repository", () => |
| 62 | Effect.gen(function* () { |
| 63 | const { nested } = yield* makeRepo("bare"); |
| 64 | assert.equal(yield* resolveGitWorktreePath(nested), undefined); |
| 65 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 66 | ); |
| 67 | |
| 68 | it.effect("reports a submodule as not a linked worktree", () => |
| 69 | Effect.gen(function* () { |
| 70 | const { nested } = yield* makeRepo("submodule"); |
| 71 | assert.equal(yield* resolveGitWorktreePath(nested), undefined); |
| 72 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 73 | ); |
| 74 | |
| 75 | it.effect("reports a .git file without a usable gitdir pointer", () => |
| 76 | Effect.gen(function* () { |
| 77 | const { nested } = yield* makeRepo("unreadable-git-file"); |
| 78 | assert.equal(yield* resolveGitWorktreePath(nested), undefined); |
| 79 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 80 | ); |
| 81 | |
| 82 | it.effect("finds a worktree of a bare repository", () => |
| 83 | Effect.gen(function* () { |
| 84 | const { root, nested } = yield* makeRepo("bare-repo-worktree"); |
| 85 | assert.equal(yield* resolveGitWorktreePath(nested), NodePath.resolve(root)); |
| 86 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 87 | ); |
| 88 | |
| 89 | it.effect("finds a worktree whose common dir is not named .git", () => |
| 90 | Effect.gen(function* () { |
| 91 | const { root, nested } = yield* makeRepo("custom-common-dir-worktree"); |
| 92 | assert.equal(yield* resolveGitWorktreePath(nested), NodePath.resolve(root)); |
| 93 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 94 | ); |
| 95 | }); |
| 96 | |
| 97 | describe("resolveWorktreeT3Home", () => { |
| 98 | it.effect("answers with .t3 before the dev runner creates it", () => |
| 99 | Effect.gen(function* () { |
| 100 | const { root, nested } = yield* makeRepo("worktree"); |
| 101 | const home = yield* resolveWorktreeT3Home(nested); |
| 102 | assert.equal(home, NodePath.join(NodePath.resolve(root), ".t3")); |
| 103 | assert.isFalse(NodeFS.existsSync(home ?? "")); |
| 104 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 105 | ); |
| 106 | }); |