8ca2230675
git.commit() was awaited without a try in both agentic commit routes and the legacy pipeline, so a refusing pre-commit/commit-msg hook escaped as an uncaught GitCommandError. In a bundled build Bun renders that as kilobytes of minified source. The split loop also threw out of its order loop, reporting no progress. The empty-staged-tree early return fired before args.push was read, so 'omp commit --push' on a clean tree exited 0 without pushing. Route commit/push failures through a shared execute helper: abortOnGitFailure prints the hook's own message (plus split-plan progress) and throws a CommitAbortedError the command maps to exit 1; pushOrAbort pushes existing commits when the tree is clean and reports refused pushes cleanly. Fixes #7834
74 lines
2.8 KiB
TypeScript
74 lines
2.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "bun:test";
|
|
import CommitCommand from "@oh-my-pi/pi-coding-agent/commands/commit";
|
|
import * as commitModule from "@oh-my-pi/pi-coding-agent/commit";
|
|
import * as themeModule from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
import { postmortem } from "@oh-my-pi/pi-utils";
|
|
|
|
describe("omp commit command lifecycle (issue #1041)", () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("forces process exit after the commit pipeline resolves", async () => {
|
|
const initThemeSpy = vi.spyOn(themeModule, "initTheme").mockResolvedValue(undefined);
|
|
const runCommitSpy = vi.spyOn(commitModule, "runCommitCommand").mockResolvedValue(undefined);
|
|
// Stub postmortem.quit so it records the exit code without actually
|
|
// terminating the test runner. Resolves immediately — the production
|
|
// implementation never returns, but the contract under test is that
|
|
// the call happens at all.
|
|
const quitSpy = vi.spyOn(postmortem, "quit").mockResolvedValue(undefined);
|
|
|
|
const command = new CommitCommand([], {
|
|
bin: "omp",
|
|
version: "0.0.0-test",
|
|
commands: new Map(),
|
|
});
|
|
|
|
await command.run();
|
|
|
|
expect(initThemeSpy).toHaveBeenCalledTimes(1);
|
|
expect(runCommitSpy).toHaveBeenCalledTimes(1);
|
|
// Quit must come after the pipeline so we cannot regress the order.
|
|
expect(runCommitSpy.mock.invocationCallOrder[0]).toBeLessThan(quitSpy.mock.invocationCallOrder[0]);
|
|
expect(quitSpy).toHaveBeenCalledWith(0);
|
|
});
|
|
|
|
it("does not convert commit pipeline failures into exit 0", async () => {
|
|
const initThemeSpy = vi.spyOn(themeModule, "initTheme").mockResolvedValue(undefined);
|
|
const runCommitSpy = vi
|
|
.spyOn(commitModule, "runCommitCommand")
|
|
.mockRejectedValue(new Error("commit was not created"));
|
|
const quitSpy = vi.spyOn(postmortem, "quit").mockResolvedValue(undefined);
|
|
|
|
const command = new CommitCommand([], {
|
|
bin: "omp",
|
|
version: "0.0.0-test",
|
|
commands: new Map(),
|
|
});
|
|
|
|
await expect(command.run()).rejects.toThrow("commit was not created");
|
|
|
|
expect(initThemeSpy).toHaveBeenCalledTimes(1);
|
|
expect(runCommitSpy).toHaveBeenCalledTimes(1);
|
|
expect(quitSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("maps CommitAbortedError to exit code 1 without rethrowing (issue #7834)", async () => {
|
|
vi.spyOn(themeModule, "initTheme").mockResolvedValue(undefined);
|
|
vi.spyOn(commitModule, "runCommitCommand").mockRejectedValue(new commitModule.CommitAbortedError());
|
|
const quitSpy = vi.spyOn(postmortem, "quit").mockResolvedValue(undefined);
|
|
|
|
const command = new CommitCommand([], {
|
|
bin: "omp",
|
|
version: "0.0.0-test",
|
|
commands: new Map(),
|
|
});
|
|
|
|
// A hook refusal is already reported with a readable message; the command
|
|
// must exit non-zero rather than let the runtime dump the error.
|
|
await command.run();
|
|
|
|
expect(quitSpy).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|