import path from "path"; import { DEFAULT_IGNORE, GIT_REPO_COMMAND, HG_REPO_COMMAND, isInGitRepository, isInMercurialRepository, tryGitInit, } from "../src/utils/git"; import childProcess from "child_process"; import { setupTestFixtures } from "@turbo/test-utils"; describe("git", () => { // just to make sure this doesn't get lost it("default .gitignore includes .turbo", async () => { expect(DEFAULT_IGNORE).toContain(".turbo"); }); describe("isInGitRepository", () => { it("returns true when in a repo", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValue("true"); const result = isInGitRepository(); expect(result).toBe(true); expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); it("returns false when not in a repo", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockImplementation(() => { throw new Error( "fatal: not a git repository (or any of the parent directories): .git" ); }); const result = isInGitRepository(); expect(result).toBe(false); expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); it("returns false on error", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockImplementation(() => { throw new Error("unknown error"); }); const result = isInGitRepository(); expect(result).toBe(false); expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); }); describe("isInMercurialRepository", () => { it("returns true when in a repo", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValue("true"); const result = isInMercurialRepository(); expect(result).toBe(true); expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); it("returns false when not in a repo", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockImplementation(() => { throw new Error("abort: no repository found (.hg not found)"); }); const result = isInMercurialRepository(); expect(result).toBe(false); expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); it("returns false on error", async () => { const mockExecSync = jest .spyOn(childProcess, "execSync") .mockImplementation(() => { throw new Error("unknown error"); }); const result = isInMercurialRepository(); expect(result).toBe(false); expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { stdio: "ignore", }); mockExecSync.mockRestore(); }); }); describe("tryGitInit", () => { const { useFixture } = setupTestFixtures({ directory: path.join(__dirname, "../"), }); it("inits a repo succesfully", async () => { const { root } = useFixture({ fixture: `git` }); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValueOnce("git version 2.38.1") .mockImplementationOnce(() => { throw new Error( "fatal: not a git repository (or any of the parent directories): .git" ); }) .mockImplementationOnce(() => { throw new Error("abort: no repository found (.hg not found)"); }) .mockReturnValue("success"); const result = tryGitInit(root, "test commit"); expect(result).toBe(true); const calls = [ "git --version", "git init", "git checkout -b main", "git add -A", 'git commit -m "test commit"', ]; expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2); calls.forEach((call) => { expect(mockExecSync).toHaveBeenCalledWith(call, { stdio: "ignore", }); }); mockExecSync.mockRestore(); }); it("skips init if already in a repo", async () => { const { root } = useFixture({ fixture: `git` }); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValueOnce("git version 2.38.1") .mockReturnValueOnce("true") .mockReturnValue("success"); const result = tryGitInit(root, "test commit"); expect(result).toBe(false); const calls = ["git --version"]; // 1 call for git --version, 1 call for isInGitRepository expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 1); calls.forEach((call) => { expect(mockExecSync).toHaveBeenCalledWith(call, { stdio: "ignore", }); }); mockExecSync.mockRestore(); }); it("returns false on unexpected error", async () => { const { root } = useFixture({ fixture: `git` }); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockImplementationOnce(() => { throw new Error("fatal: unknown command git"); }); const result = tryGitInit(root, "test commit"); expect(result).toBe(false); const calls = ["git --version"]; expect(mockExecSync).toHaveBeenCalledTimes(calls.length); calls.forEach((call) => { expect(mockExecSync).toHaveBeenCalledWith(call, { stdio: "ignore", }); }); mockExecSync.mockRestore(); }); it("cleans up from partial init on failure", async () => { const { root } = useFixture({ fixture: `git` }); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValueOnce("git version 2.38.1") .mockImplementationOnce(() => { throw new Error( "fatal: not a git repository (or any of the parent directories): .git" ); }) .mockImplementationOnce(() => { throw new Error("abort: no repository found (.hg not found)"); }) .mockReturnValueOnce("success") .mockReturnValueOnce("success") .mockImplementationOnce(() => { throw new Error("fatal: could not add files"); }); const result = tryGitInit(root, "test commit"); expect(result).toBe(false); const calls = [ "git --version", "git init", "git checkout -b main", "git add -A", ]; expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2); calls.forEach((call) => { expect(mockExecSync).toHaveBeenCalledWith(call, { stdio: "ignore", }); }); mockExecSync.mockRestore(); }); }); });