"use strict";
/**
 * Tests for Git Utilities
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const child_process_1 = require("child_process");
const git_1 = require("./git");
describe('Git', () => {
    let testDir;
    let git;
    beforeEach(() => {
        testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-test-'));
        git = new git_1.Git(testDir);
    });
    afterEach(() => {
        if (fs.existsSync(testDir)) {
            fs.rmSync(testDir, { recursive: true, force: true });
        }
    });
    /**
     * Helper to initialize a git repo
     */
    const initRepo = () => {
        (0, child_process_1.execSync)('git init', { cwd: testDir });
        (0, child_process_1.execSync)('git config user.email "test@example.com"', { cwd: testDir });
        (0, child_process_1.execSync)('git config user.name "Test User"', { cwd: testDir });
    };
    /**
     * Helper to create a file and commit it
     */
    const createFileAndCommit = (filename, content, message) => {
        fs.writeFileSync(path.join(testDir, filename), content);
        (0, child_process_1.execSync)(`git add ${filename}`, { cwd: testDir });
        (0, child_process_1.execSync)(`git commit -m "${message}"`, { cwd: testDir });
    };
    describe('isGitRepo', () => {
        it('should return false for non-git directory', () => {
            expect(git.isGitRepo()).toBe(false);
        });
        it('should return true for git repository', () => {
            initRepo();
            expect(git.isGitRepo()).toBe(true);
        });
    });
    describe('getStatus', () => {
        it('should return not a repo for non-git directory', () => {
            const status = git.getStatus();
            expect(status.isRepo).toBe(false);
            expect(status.isClean).toBe(false);
            expect(status.currentBranch).toBe('');
            expect(status.uncommittedChanges).toEqual([]);
        });
        it('should return clean status for clean repo', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            const status = git.getStatus();
            expect(status.isRepo).toBe(true);
            expect(status.isClean).toBe(true);
            expect(status.currentBranch).toBe('main');
            expect(status.uncommittedChanges).toEqual([]);
        });
        it('should detect uncommitted changes', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            // Make changes
            fs.writeFileSync(path.join(testDir, 'test.txt'), 'modified content');
            const status = git.getStatus();
            expect(status.isRepo).toBe(true);
            expect(status.isClean).toBe(false);
            expect(status.uncommittedChanges.length).toBeGreaterThan(0);
        });
        it('should detect untracked files', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            // Add untracked file
            fs.writeFileSync(path.join(testDir, 'untracked.txt'), 'untracked');
            const status = git.getStatus();
            expect(status.isClean).toBe(false);
            expect(status.uncommittedChanges.length).toBeGreaterThan(0);
        });
    });
    describe('isClean', () => {
        it('should return false for non-git directory', () => {
            expect(git.isClean()).toBe(false);
        });
        it('should return true for clean repo', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(git.isClean()).toBe(true);
        });
        it('should return false when there are uncommitted changes', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            fs.writeFileSync(path.join(testDir, 'test.txt'), 'modified');
            expect(git.isClean()).toBe(false);
        });
    });
    describe('getCurrentCommit', () => {
        it('should throw error for non-git directory', () => {
            expect(() => git.getCurrentCommit()).toThrow(git_1.GitError);
        });
        it('should return current commit hash', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            const commit = git.getCurrentCommit();
            expect(commit).toMatch(/^[a-f0-9]{40}$/);
        });
    });
    describe('createTag', () => {
        it('should throw error for non-git directory', () => {
            expect(() => git.createTag('v1.0.0', 'Release 1.0.0')).toThrow(git_1.GitError);
            expect(() => git.createTag('v1.0.0', 'Release 1.0.0')).toThrow('Not a git repository');
        });
        it('should create a tag', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            const tags = git.listTags();
            expect(tags.length).toBe(1);
            expect(tags[0].tag).toBe('v1.0.0');
        });
        it('should throw error if tag already exists', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            expect(() => git.createTag('v1.0.0', 'Duplicate')).toThrow(git_1.GitError);
            expect(() => git.createTag('v1.0.0', 'Duplicate')).toThrow("Tag 'v1.0.0' already exists");
        });
    });
    describe('listTags', () => {
        it('should return empty array for non-git directory', () => {
            expect(git.listTags()).toEqual([]);
        });
        it('should return empty array when no tags exist', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(git.listTags()).toEqual([]);
        });
        it('should list all tags', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            git.createTag('v1.1.0', 'Release 1.1.0');
            const tags = git.listTags();
            expect(tags.length).toBe(2);
            // Should be most recent first (reversed)
            expect(tags[0].tag).toBe('v1.1.0');
            expect(tags[1].tag).toBe('v1.0.0');
        });
        it('should include tag metadata', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            const tags = git.listTags();
            expect(tags[0].tag).toBe('v1.0.0');
            expect(tags[0].commit).toMatch(/^[a-f0-9]+$/);
            expect(tags[0].date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
            expect(tags[0].message).toBe('Release 1.0.0');
        });
    });
    describe('getTag', () => {
        it('should return null for non-existent tag', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(git.getTag('v1.0.0')).toBeNull();
        });
        it('should return tag details', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            const tag = git.getTag('v1.0.0');
            expect(tag).not.toBeNull();
            expect(tag?.tag).toBe('v1.0.0');
            expect(tag?.message).toBe('Release 1.0.0');
        });
    });
    describe('checkout', () => {
        it('should throw error for non-git directory', () => {
            expect(() => git.checkout('v1.0.0')).toThrow(git_1.GitError);
            expect(() => git.checkout('v1.0.0')).toThrow('Not a git repository');
        });
        it('should throw error if tag does not exist', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(() => git.checkout('v1.0.0')).toThrow(git_1.GitError);
            expect(() => git.checkout('v1.0.0')).toThrow("Tag 'v1.0.0' does not exist");
        });
        it('should throw error if repo has uncommitted changes', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            // Make uncommitted changes
            fs.writeFileSync(path.join(testDir, 'test.txt'), 'modified');
            expect(() => git.checkout('v1.0.0')).toThrow(git_1.GitError);
            expect(() => git.checkout('v1.0.0')).toThrow('Repository has uncommitted changes');
        });
        it('should checkout tag successfully', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            // Make another commit
            createFileAndCommit('test2.txt', 'content2', 'Second commit');
            // Checkout tag
            git.checkout('v1.0.0');
            // Verify we're in detached HEAD state (not on main branch)
            const status = git.getStatus();
            expect(status.currentBranch).toBe('HEAD');
            // Verify test2.txt doesn't exist at this tag
            expect(fs.existsSync(path.join(testDir, 'test2.txt'))).toBe(false);
        });
    });
    describe('getFileAtTag', () => {
        it('should return null for non-git directory', () => {
            expect(git.getFileAtTag('v1.0.0', 'test.txt')).toBeNull();
        });
        it('should return null for non-existent tag', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(git.getFileAtTag('v1.0.0', 'test.txt')).toBeNull();
        });
        it('should return null for non-existent file', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            expect(git.getFileAtTag('v1.0.0', 'nonexistent.txt')).toBeNull();
        });
        it('should return file content at tag', () => {
            initRepo();
            createFileAndCommit('test.txt', 'original content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            // Modify file
            createFileAndCommit('test.txt', 'modified content', 'Modify file');
            // Get file at tag should return original content
            const content = git.getFileAtTag('v1.0.0', 'test.txt');
            expect(content).toBe('original content');
        });
    });
    describe('commit', () => {
        it('should throw error for non-git directory', () => {
            expect(() => git.commit('Test commit')).toThrow(git_1.GitError);
            expect(() => git.commit('Test commit')).toThrow('Not a git repository');
        });
        it('should throw error when no changes to commit', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(() => git.commit('Empty commit')).toThrow(git_1.GitError);
            expect(() => git.commit('Empty commit')).toThrow('No changes to commit');
        });
        it('should create commit with changes', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            // Make changes
            fs.writeFileSync(path.join(testDir, 'test.txt'), 'modified content');
            git.commit('Update file');
            const status = git.getStatus();
            expect(status.isClean).toBe(true);
        });
    });
    describe('diff', () => {
        it('should throw error for non-git directory', () => {
            expect(() => git.diff('v1.0.0', 'v2.0.0')).toThrow(git_1.GitError);
        });
        it('should return diff between tags', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('v1.0.0', 'Release 1.0.0');
            createFileAndCommit('test2.txt', 'content2', 'Add file');
            git.createTag('v2.0.0', 'Release 2.0.0');
            const diff = git.diff('v1.0.0', 'v2.0.0');
            expect(diff).toContain('test2.txt');
        });
    });
    describe('isValidSemver', () => {
        it('should validate correct semver versions', () => {
            expect(git_1.Git.isValidSemver('1.0.0')).toBe(true);
            expect(git_1.Git.isValidSemver('0.0.1')).toBe(true);
            expect(git_1.Git.isValidSemver('10.20.30')).toBe(true);
            expect(git_1.Git.isValidSemver('1.0.0-alpha')).toBe(true);
            expect(git_1.Git.isValidSemver('1.0.0-alpha.1')).toBe(true);
            expect(git_1.Git.isValidSemver('1.0.0+20130313144700')).toBe(true);
            expect(git_1.Git.isValidSemver('1.0.0-beta+exp.sha.5114f85')).toBe(true);
        });
        it('should reject invalid semver versions', () => {
            expect(git_1.Git.isValidSemver('1')).toBe(false);
            expect(git_1.Git.isValidSemver('1.0')).toBe(false);
            expect(git_1.Git.isValidSemver('v1.0.0')).toBe(false);
            expect(git_1.Git.isValidSemver('1.0.0.')).toBe(false);
            expect(git_1.Git.isValidSemver('a.b.c')).toBe(false);
            expect(git_1.Git.isValidSemver('')).toBe(false);
            expect(git_1.Git.isValidSemver('1.0.0 ')).toBe(false);
        });
    });
    describe('compareSemver', () => {
        it('should compare major versions', () => {
            expect(git_1.Git.compareSemver('2.0.0', '1.0.0')).toBe(1);
            expect(git_1.Git.compareSemver('1.0.0', '2.0.0')).toBe(-1);
            expect(git_1.Git.compareSemver('1.0.0', '1.0.0')).toBe(0);
        });
        it('should compare minor versions', () => {
            expect(git_1.Git.compareSemver('1.2.0', '1.1.0')).toBe(1);
            expect(git_1.Git.compareSemver('1.1.0', '1.2.0')).toBe(-1);
            expect(git_1.Git.compareSemver('1.1.0', '1.1.0')).toBe(0);
        });
        it('should compare patch versions', () => {
            expect(git_1.Git.compareSemver('1.0.2', '1.0.1')).toBe(1);
            expect(git_1.Git.compareSemver('1.0.1', '1.0.2')).toBe(-1);
            expect(git_1.Git.compareSemver('1.0.1', '1.0.1')).toBe(0);
        });
        it('should handle complex version comparisons', () => {
            expect(git_1.Git.compareSemver('2.0.0', '1.9.9')).toBe(1);
            expect(git_1.Git.compareSemver('1.10.0', '1.9.0')).toBe(1);
            expect(git_1.Git.compareSemver('1.0.10', '1.0.9')).toBe(1);
        });
    });
    describe('getLatestVersion', () => {
        it('should return null for non-git directory', () => {
            expect(git.getLatestVersion()).toBeNull();
        });
        it('should return null when no version tags exist', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            expect(git.getLatestVersion()).toBeNull();
        });
        it('should return null when only non-version tags exist', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('release', 'Some release');
            expect(git.getLatestVersion()).toBeNull();
        });
        it('should return latest version tag', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('1.0.0', 'Release 1.0.0');
            createFileAndCommit('test2.txt', 'content2', 'Second commit');
            git.createTag('2.0.0', 'Release 2.0.0');
            const latest = git.getLatestVersion();
            expect(latest).toBe('2.0.0');
        });
        it('should ignore non-semver tags', () => {
            initRepo();
            createFileAndCommit('test.txt', 'content', 'Initial commit');
            git.createTag('1.0.0', 'Release 1.0.0');
            createFileAndCommit('test2.txt', 'content2', 'Second commit');
            git.createTag('latest', 'Latest release');
            const latest = git.getLatestVersion();
            expect(latest).toBe('1.0.0');
        });
    });
});
//# sourceMappingURL=git.test.js.map