178 lines
5.7 KiB
TypeScript
178 lines
5.7 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
|
const mockVersion = vi.fn();
|
|
const mockCheckIsRepo = vi.fn();
|
|
const mockRevparse = vi.fn();
|
|
const mockStatus = vi.fn();
|
|
const mockInit = vi.fn();
|
|
const mockRaw = vi.fn();
|
|
const mockAdd = vi.fn();
|
|
const mockGetRemotes = vi.fn();
|
|
const mockAddRemote = vi.fn();
|
|
const mockRemote = vi.fn();
|
|
|
|
vi.mock('simple-git', () => ({
|
|
simpleGit: vi.fn(() => ({
|
|
version: mockVersion,
|
|
checkIsRepo: mockCheckIsRepo,
|
|
revparse: mockRevparse,
|
|
status: mockStatus,
|
|
init: mockInit,
|
|
raw: mockRaw,
|
|
add: mockAdd,
|
|
getRemotes: mockGetRemotes,
|
|
addRemote: mockAddRemote,
|
|
remote: mockRemote,
|
|
})),
|
|
}));
|
|
|
|
import { GitEngine } from '../../src/main/engine/GitEngine';
|
|
|
|
describe('GitEngine', () => {
|
|
let gitEngine: GitEngine;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
gitEngine = new GitEngine();
|
|
});
|
|
|
|
describe('checkAvailability', () => {
|
|
it('should return gitFound true with version when git is available', async () => {
|
|
mockVersion.mockResolvedValue({
|
|
major: 2,
|
|
minor: 49,
|
|
patch: 0,
|
|
agent: 'git/version',
|
|
installed: true,
|
|
});
|
|
|
|
const result = await gitEngine.checkAvailability();
|
|
|
|
expect(result).toEqual({ gitFound: true, version: '2.49.0' });
|
|
});
|
|
|
|
it('should return gitFound false when git is not available', async () => {
|
|
mockVersion.mockRejectedValue(new Error('git: command not found'));
|
|
|
|
const result = await gitEngine.checkAvailability();
|
|
|
|
expect(result).toEqual({ gitFound: false });
|
|
});
|
|
});
|
|
|
|
describe('getRepoState', () => {
|
|
it('should return non-repo state when project is not a git repository', async () => {
|
|
mockCheckIsRepo.mockResolvedValue(false);
|
|
|
|
const result = await gitEngine.getRepoState('/tmp/project');
|
|
|
|
expect(result).toEqual({
|
|
isRepo: false,
|
|
hasRemote: false,
|
|
});
|
|
});
|
|
|
|
it('should return repo details for a valid repository', async () => {
|
|
mockCheckIsRepo.mockResolvedValue(true);
|
|
mockRevparse.mockResolvedValue('/tmp/project');
|
|
mockStatus.mockResolvedValue({
|
|
current: 'main',
|
|
tracking: 'origin/main',
|
|
});
|
|
|
|
const result = await gitEngine.getRepoState('/tmp/project');
|
|
|
|
expect(result).toEqual({
|
|
isRepo: true,
|
|
rootPath: '/tmp/project',
|
|
currentBranch: 'main',
|
|
hasRemote: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getStatus', () => {
|
|
it('should normalize changed files and counts from git status', async () => {
|
|
mockStatus.mockResolvedValue({
|
|
not_added: ['new-file.md'],
|
|
modified: ['edited.md'],
|
|
deleted: ['removed.md'],
|
|
renamed: [{ from: 'old.md', to: 'new.md' }],
|
|
created: ['staged.md'],
|
|
});
|
|
|
|
const result = await gitEngine.getStatus('/tmp/project');
|
|
|
|
expect(result.counts).toEqual({
|
|
untracked: 1,
|
|
modified: 1,
|
|
deleted: 1,
|
|
renamed: 1,
|
|
staged: 1,
|
|
total: 5,
|
|
});
|
|
expect(result.files).toEqual([
|
|
{ path: 'new-file.md', status: 'untracked' },
|
|
{ path: 'edited.md', status: 'modified' },
|
|
{ path: 'removed.md', status: 'deleted' },
|
|
{ path: 'new.md', status: 'renamed', previousPath: 'old.md' },
|
|
{ path: 'staged.md', status: 'staged' },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('initializeRepo', () => {
|
|
it('should initialize git repo, configure lfs and track image patterns', async () => {
|
|
mockVersion.mockResolvedValue({ major: 2, minor: 49, patch: 0, agent: 'git/version', installed: true });
|
|
mockInit.mockResolvedValue(undefined);
|
|
mockRaw.mockResolvedValue('ok');
|
|
mockAdd.mockResolvedValue(undefined);
|
|
|
|
const result = await gitEngine.initializeRepo('/tmp/project');
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(mockInit).toHaveBeenCalled();
|
|
expect(mockRaw).toHaveBeenCalledWith(['lfs', 'install', '--local']);
|
|
expect(mockAdd).toHaveBeenCalledWith('.gitattributes');
|
|
expect(mockAddRemote).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should configure origin remote when a remote url is provided', async () => {
|
|
mockVersion.mockResolvedValue({ major: 2, minor: 49, patch: 0, agent: 'git/version', installed: true });
|
|
mockInit.mockResolvedValue(undefined);
|
|
mockRaw.mockResolvedValue('ok');
|
|
mockAdd.mockResolvedValue(undefined);
|
|
mockGetRemotes.mockResolvedValue([]);
|
|
mockAddRemote.mockResolvedValue(undefined);
|
|
|
|
const result = await gitEngine.initializeRepo('/tmp/project', 'https://github.com/example/repo.git');
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(mockGetRemotes).toHaveBeenCalledWith(true);
|
|
expect(mockAddRemote).toHaveBeenCalledWith('origin', 'https://github.com/example/repo.git');
|
|
});
|
|
|
|
it('should return explicit git missing guidance when git is unavailable', async () => {
|
|
mockVersion.mockRejectedValue(new Error('git: command not found'));
|
|
|
|
const result = await gitEngine.initializeRepo('/tmp/project');
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.code).toBe('git-missing');
|
|
expect(result.error).toContain('install Git');
|
|
});
|
|
|
|
it('should return explicit git-lfs missing guidance when lfs command fails', async () => {
|
|
mockVersion.mockResolvedValue({ major: 2, minor: 49, patch: 0, agent: 'git/version', installed: true });
|
|
mockInit.mockResolvedValue(undefined);
|
|
mockRaw.mockRejectedValue(new Error('git: lfs is not a git command'));
|
|
|
|
const result = await gitEngine.initializeRepo('/tmp/project');
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.code).toBe('git-lfs-missing');
|
|
expect(result.error).toContain('install Git LFS');
|
|
});
|
|
});
|
|
});
|