import path from "node:path"; import { describe, expect, test } from "vitest"; import { GitUpdateService, UPDATE_REMOTE_URL, type CommandResult, type CommandRunner, } from "../../src/updater/git-update-service.js"; function result(stdout = "", code = 0, stderr = ""): CommandResult { return { code, stdout, stderr }; } function fakeRunner(responses: Map): CommandRunner { return async (command, args) => responses.get(`${command} ${args.join(" ")}`) ?? result("", 1, "unexpected command"); } describe("GitUpdateService", () => { test("checks the currently checked-out branch", async () => { const root = path.resolve("."); const runner = fakeRunner( new Map([ ["git branch --show-current", result("feature/demo\n")], [ `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/feature/demo:refs/remotes/xo-updater/feature/demo`, result(), ], ["git rev-parse HEAD", result("aaa\n")], ["git rev-parse refs/remotes/xo-updater/feature/demo", result("bbb\n")], ["git status --porcelain", result()], ["git merge-base --is-ancestor aaa bbb", result()], ["git log -1 --format=%s bbb", result("Useful update\n")], ]), ); const update = await new GitUpdateService(root, runner).check(); expect(update).toMatchObject({ status: "available", branch: "feature/demo", currentCommit: "aaa", targetCommit: "bbb", canApply: true, }); }); test("reports an available update but blocks applying over local changes", async () => { const root = path.resolve("."); const runner = fakeRunner( new Map([ ["git branch --show-current", result("main\n")], [ `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/main:refs/remotes/xo-updater/main`, result(), ], ["git rev-parse HEAD", result("aaa\n")], ["git rev-parse refs/remotes/xo-updater/main", result("bbb\n")], ["git status --porcelain", result(" M src/example.ts\n")], ["git merge-base --is-ancestor aaa bbb", result()], ["git log -1 --format=%s bbb", result("Update\n")], ]), ); const update = await new GitUpdateService(root, runner).check(); expect(update).toMatchObject({ status: "available", canApply: false }); }); test("does not replace commits when the local branch is ahead", async () => { const root = path.resolve("."); const runner = fakeRunner( new Map([ ["git branch --show-current", result("main\n")], [ `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/main:refs/remotes/xo-updater/main`, result(), ], ["git rev-parse HEAD", result("bbb\n")], ["git rev-parse refs/remotes/xo-updater/main", result("aaa\n")], ["git status --porcelain", result()], ["git merge-base --is-ancestor bbb aaa", result("", 1)], ["git merge-base --is-ancestor aaa bbb", result()], ]), ); await expect( new GitUpdateService(root, runner).check(), ).resolves.toMatchObject({ status: "up-to-date", branch: "main", currentCommit: "bbb", }); }); });