import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import type { GitHubClient } from "../../src/github/client.js"; import { handleRebaseAfterSquashMerge } from "../../src/tools/rebase-after-squash-merge/handler.js"; const byStep = ( commands: Array<{ step: number; command: string; description: string }>, step: number, ) => commands.find((c) => c.step === step)!; describe("handleRebaseAfterSquashMerge", () => { let mockClient: GitHubClient; let mockOctokit: any; beforeEach(() => { mockOctokit = { pulls: { get: vi.fn(), }, }; mockClient = { getOctokit: vi.fn().mockReturnValue(mockOctokit), } as any; }); afterEach(() => { vi.clearAllMocks(); }); it("should return basic rebase commands with default target branch", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "main" }, head: { ref: "feature-branch" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", }); // Called with normalized params expect(mockOctokit.pulls.get).toHaveBeenCalledTimes(1); expect(mockOctokit.pulls.get).toHaveBeenCalledWith({ owner: "owner", repo: "repo", pull_number: 123, }); expect(result.pr).toBe("owner/repo#123"); expect(result.analysis.upstream_pr).toBeUndefined(); expect(result.analysis.detected_squash_merge).toBe(false); expect(result.commands).toHaveLength(3); expect(byStep(result.commands, 1).command).toBe("git fetch origin"); expect(byStep(result.commands, 2).command).toContain( "git rebase --onto origin/main", ); expect(byStep(result.commands, 2).command).toContain( "", ); expect(byStep(result.commands, 2).command).toContain("feature-branch"); expect(byStep(result.commands, 3).command).toBe( "git push --force-with-lease origin feature-branch", ); expect(result.summary.action_required).toBe(true); expect(result.summary.reason).toBe( "Manual upstream commit identification needed in this phase", ); }); it("should use custom target branch when provided", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "main" }, head: { ref: "feature-branch" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", target_branch: "develop", }); expect(byStep(result.commands, 2).command).toContain( "git rebase --onto origin/develop", ); expect(byStep(result.commands, 2).command).toContain("feature-branch"); }); it("should include upstream PR in analysis when provided", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "main" }, head: { ref: "feature-branch" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", upstream_pr: "owner/repo#456", }); expect(result.analysis.upstream_pr).toBe("owner/repo#456"); expect(result.summary.action_required).toBe(true); }); // This test specifically validates command descriptions - different from the basic command structure test above it("should provide detailed command descriptions", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "main" }, head: { ref: "feature-branch" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", }); expect(result.commands[0].description).toBe( "Fetch latest changes from remote", ); expect(result.commands[1].description).toContain( "Rebase using --onto to skip squash-merged commits", ); expect(result.commands[2].description).toBe("Update remote branch"); }); it("should handle different branch names", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "develop" }, head: { ref: "my-feature" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", }); expect(result.commands[1].command).toContain( "git rebase --onto origin/develop", ); expect(result.commands[2].command).toBe( "git push --force-with-lease origin my-feature", ); }); it("should always indicate action is required", async () => { mockOctokit.pulls.get.mockResolvedValue({ data: { base: { ref: "main" }, head: { ref: "feature-branch" }, }, }); const result = await handleRebaseAfterSquashMerge(mockClient, { pr: "owner/repo#123", }); expect(result.summary.action_required).toBe(true); expect(result.summary.reason).toBe( "Manual upstream commit identification needed in this phase", ); }); });