import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { runGitViaPty, GitNotInitializedError } from "../git.js";
// Mock node-pty
let mockOnData = null;
let mockOnExit = null;
const mockWrite = vi.fn();
const mockKill = vi.fn();
vi.mock("node-pty", () => ({
    spawn: vi.fn(() => {
        return {
            onData: (cb) => {
                mockOnData = cb;
            },
            onExit: (cb) => {
                mockOnExit = cb;
            },
            write: mockWrite,
            kill: mockKill,
        };
    }),
}));
// Suppress logger output in tests
vi.mock("../../utils/logger.js", () => ({
    logger: {
        debug: vi.fn(),
        info: vi.fn(),
        warn: vi.fn(),
        error: vi.fn(),
    },
}));
// Mock credential handling
const mockRequestCredential = vi.fn();
vi.mock("../../handlers/git/credentials.js", () => ({
    detectCredentialPrompt: (data) => {
        if (data.includes("Password")) {
            return { type: "password", prompt: data.trim() };
        }
        if (data.includes("Enter passphrase for key")) {
            return { type: "passphrase", prompt: data.trim() };
        }
        return null;
    },
    requestCredential: (...args) => mockRequestCredential(...args),
}));
describe("runGitViaPty", () => {
    beforeEach(() => {
        mockOnData = null;
        mockOnExit = null;
        mockWrite.mockClear();
        mockKill.mockClear();
        mockRequestCredential.mockClear();
    });
    afterEach(() => {
        vi.restoreAllMocks();
    });
    it("should resolve on successful push", async () => {
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin");
        // Simulate PTY output and exit
        mockOnData?.("Everything up-to-date\n");
        mockOnExit?.({ exitCode: 0 });
        const result = await promise;
        expect(result).toEqual({ remote: "origin", branch: "main", pushed: true });
    });
    it("should reject on push failure", async () => {
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin");
        mockOnData?.("[rejected] main -> main (non-fast-forward)\n");
        mockOnExit?.({ exitCode: 1 });
        await expect(promise).rejects.toThrow("[rejected]");
    });
    it("should detect credential prompts and forward to client", async () => {
        mockRequestCredential.mockResolvedValue({ value: "mypassword", cancelled: false });
        const mockSend = vi.fn();
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin", "req-1", mockSend);
        // Simulate credential prompt
        mockOnData?.("Password for 'https://user@github.com':");
        // Allow the credential request promise to settle
        await vi.waitFor(() => {
            expect(mockRequestCredential).toHaveBeenCalledWith("req-1", "password", "Password for 'https://user@github.com':", mockSend);
        });
        // Verify the credential value was written to PTY
        await vi.waitFor(() => {
            expect(mockWrite).toHaveBeenCalledWith("mypassword\n");
        });
        // Complete the push
        mockOnExit?.({ exitCode: 0 });
        const result = await promise;
        expect(result.pushed).toBe(true);
    });
    it("should kill PTY when credential prompt is cancelled", async () => {
        mockRequestCredential.mockResolvedValue({ value: "", cancelled: true });
        const mockSend = vi.fn();
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin", "req-2", mockSend);
        // Simulate credential prompt
        mockOnData?.("Enter passphrase for key '/home/user/.ssh/id_rsa':");
        // Allow the credential request promise to settle
        await vi.waitFor(() => {
            expect(mockRequestCredential).toHaveBeenCalled();
        });
        // Verify PTY was killed
        await vi.waitFor(() => {
            expect(mockKill).toHaveBeenCalled();
        });
        // Simulate exit after kill
        mockOnExit?.({ exitCode: 1 });
        await expect(promise).rejects.toThrow();
    });
    it("should handle passphrase prompts", async () => {
        mockRequestCredential.mockResolvedValue({ value: "mypassphrase", cancelled: false });
        const mockSend = vi.fn();
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin", "req-3", mockSend);
        mockOnData?.("Enter passphrase for key '/home/user/.ssh/id_rsa':");
        await vi.waitFor(() => {
            expect(mockRequestCredential).toHaveBeenCalledWith("req-3", "passphrase", "Enter passphrase for key '/home/user/.ssh/id_rsa':", mockSend);
        });
        await vi.waitFor(() => {
            expect(mockWrite).toHaveBeenCalledWith("mypassphrase\n");
        });
        mockOnExit?.({ exitCode: 0 });
        const result = await promise;
        expect(result.pushed).toBe(true);
    });
    it("should throw GitNotInitializedError for non-git repo", async () => {
        const promise = runGitViaPty("/not-a-repo", ["push", "origin", "main"], "main", "origin");
        mockOnData?.("fatal: not a git repository (or any of the parent directories): .git\n");
        mockOnExit?.({ exitCode: 128 });
        await expect(promise).rejects.toThrow(GitNotInitializedError);
    });
    it("should work without send function (no credential support)", async () => {
        const promise = runGitViaPty("/project", ["push", "origin", "main"], "main", "origin");
        mockOnData?.("Everything up-to-date\n");
        mockOnExit?.({ exitCode: 0 });
        const result = await promise;
        expect(result.pushed).toBe(true);
        expect(mockRequestCredential).not.toHaveBeenCalled();
    });
});
//# sourceMappingURL=git-pty.test.js.map