- Add GitEvent types (GitStatusEvent, GitCommitEvent, GitBranchEvent, GitDiffEvent) - Implement git event parsing in gitParser.ts - Support parsing git status (staged, unstaged, untracked files) - Support parsing git commits with file changes - Support parsing git branch information - Support parsing git diff output with line counts - Add comprehensive unit tests (19 tests) - All tests pass (1011 total tests) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
428 lines
11 KiB
TypeScript
428 lines
11 KiB
TypeScript
/**
|
|
* Tests for FABRIC Git Event Parser
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
isGitEvent,
|
|
parseGitEvent,
|
|
parseGitEvents,
|
|
formatGitEvent,
|
|
} from './gitParser.js';
|
|
import { LogEvent, GitEvent, GitStatusEvent, GitCommitEvent } from './types.js';
|
|
|
|
describe('isGitEvent', () => {
|
|
it('should detect git status events', () => {
|
|
const event: LogEvent = {
|
|
ts: Date.now(),
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git status',
|
|
git_status: { branch: 'main' },
|
|
};
|
|
|
|
expect(isGitEvent(event)).toBe(true);
|
|
});
|
|
|
|
it('should detect git commit events', () => {
|
|
const event: LogEvent = {
|
|
ts: Date.now(),
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git commit',
|
|
git_commit: 'abc123',
|
|
};
|
|
|
|
expect(isGitEvent(event)).toBe(true);
|
|
});
|
|
|
|
it('should detect git events from message patterns', () => {
|
|
const event: LogEvent = {
|
|
ts: Date.now(),
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Running git status',
|
|
};
|
|
|
|
expect(isGitEvent(event)).toBe(true);
|
|
});
|
|
|
|
it('should return false for non-git events', () => {
|
|
const event: LogEvent = {
|
|
ts: Date.now(),
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Regular log message',
|
|
};
|
|
|
|
expect(isGitEvent(event)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('parseGitEvent', () => {
|
|
describe('git status events', () => {
|
|
it('should parse a basic git status event', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git status',
|
|
git_type: 'status',
|
|
git_branch: 'main',
|
|
git_staged: ['file1.ts', 'file2.ts'],
|
|
git_unstaged: ['file3.ts'],
|
|
git_untracked: ['file4.ts'],
|
|
};
|
|
|
|
const result = parseGitEvent(event) as GitStatusEvent;
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.type).toBe('status');
|
|
expect(result.branch).toBe('main');
|
|
expect(result.staged).toHaveLength(2);
|
|
expect(result.unstaged).toHaveLength(1);
|
|
expect(result.untracked).toHaveLength(1);
|
|
});
|
|
|
|
it('should parse git status with tracking info', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git status',
|
|
git_status: {
|
|
branch: 'feature/auth',
|
|
tracking: 'origin/feature/auth',
|
|
ahead: 3,
|
|
behind: 1,
|
|
commit: 'abc123',
|
|
},
|
|
git_staged: [],
|
|
git_unstaged: [],
|
|
git_untracked: [],
|
|
};
|
|
|
|
const result = parseGitEvent(event) as GitStatusEvent;
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.branch).toBe('feature/auth');
|
|
expect(result.tracking).toBe('origin/feature/auth');
|
|
expect(result.ahead).toBe(3);
|
|
expect(result.behind).toBe(1);
|
|
expect(result.commit).toBe('abc123');
|
|
});
|
|
|
|
it('should parse file changes with detailed status', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git status',
|
|
git_type: 'status',
|
|
git_branch: 'main',
|
|
git_staged: [
|
|
{ path: 'added.ts', status: 'added', staged: true },
|
|
{ path: 'modified.ts', status: 'modified', staged: true },
|
|
],
|
|
git_unstaged: [
|
|
{ path: 'deleted.ts', status: 'deleted', staged: false },
|
|
],
|
|
git_untracked: ['new-file.ts'],
|
|
};
|
|
|
|
const result = parseGitEvent(event) as GitStatusEvent;
|
|
|
|
expect(result.staged[0].path).toBe('added.ts');
|
|
expect(result.staged[0].status).toBe('added');
|
|
expect(result.staged[1].path).toBe('modified.ts');
|
|
expect(result.staged[1].status).toBe('modified');
|
|
expect(result.unstaged[0].path).toBe('deleted.ts');
|
|
expect(result.unstaged[0].status).toBe('deleted');
|
|
});
|
|
});
|
|
|
|
describe('git commit events', () => {
|
|
it('should parse a basic git commit event', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git commit',
|
|
git_type: 'commit',
|
|
git_commit: 'abc123def456',
|
|
git_message: 'feat: add new feature',
|
|
git_branch: 'main',
|
|
};
|
|
|
|
const result = parseGitEvent(event) as GitCommitEvent;
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.type).toBe('commit');
|
|
expect(result.hash).toBe('abc123def456');
|
|
expect(result.message).toBe('feat: add new feature');
|
|
expect(result.branch).toBe('main');
|
|
});
|
|
|
|
it('should parse commit with author and files', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git commit',
|
|
git_commit: {
|
|
hash: 'abc123',
|
|
message: 'fix: bug fix',
|
|
author: 'John Doe',
|
|
email: 'john@example.com',
|
|
files: [
|
|
{ path: 'file1.ts', status: 'modified' },
|
|
{ path: 'file2.ts', status: 'added' },
|
|
],
|
|
},
|
|
};
|
|
|
|
const result = parseGitEvent(event) as GitCommitEvent;
|
|
|
|
expect(result.hash).toBe('abc123');
|
|
expect(result.author).toBe('John Doe');
|
|
expect(result.email).toBe('john@example.com');
|
|
expect(result.files).toHaveLength(2);
|
|
expect(result.files![0].path).toBe('file1.ts');
|
|
});
|
|
|
|
it('should return null for commit without hash', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git commit',
|
|
git_type: 'commit',
|
|
git_message: 'No hash provided',
|
|
};
|
|
|
|
const result = parseGitEvent(event);
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('git branch events', () => {
|
|
it('should parse a basic git branch event', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git branch',
|
|
git_type: 'branch',
|
|
git_branch: 'main',
|
|
};
|
|
|
|
const result = parseGitEvent(event);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result!.type).toBe('branch');
|
|
expect(result).toHaveProperty('current', 'main');
|
|
});
|
|
|
|
it('should parse branch with tracking info', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git branch',
|
|
git_branch: {
|
|
current: 'feature/test',
|
|
branches: ['main', 'feature/test', 'develop'],
|
|
tracking: 'origin/feature/test',
|
|
ahead: 2,
|
|
behind: 0,
|
|
},
|
|
};
|
|
|
|
const result = parseGitEvent(event);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result).toHaveProperty('current', 'feature/test');
|
|
expect(result).toHaveProperty('branches');
|
|
expect(result).toHaveProperty('tracking', 'origin/feature/test');
|
|
expect(result).toHaveProperty('ahead', 2);
|
|
});
|
|
});
|
|
|
|
describe('git diff events', () => {
|
|
it('should parse a basic git diff event', () => {
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git diff',
|
|
git_type: 'diff',
|
|
git_target: 'HEAD',
|
|
git_files: [
|
|
{ path: 'file1.ts', status: 'modified' },
|
|
{ path: 'file2.ts', status: 'added' },
|
|
],
|
|
git_lines_added: 45,
|
|
git_lines_deleted: 12,
|
|
};
|
|
|
|
const result = parseGitEvent(event);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result!.type).toBe('diff');
|
|
expect(result).toHaveProperty('target', 'HEAD');
|
|
expect(result).toHaveProperty('linesAdded', 45);
|
|
expect(result).toHaveProperty('linesDeleted', 12);
|
|
expect(result).toHaveProperty('files');
|
|
});
|
|
|
|
it('should truncate long diff content', () => {
|
|
const longContent = 'a'.repeat(15000);
|
|
const event: LogEvent = {
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git diff',
|
|
git_diff: {
|
|
target: 'origin/main',
|
|
content: longContent,
|
|
files: [],
|
|
},
|
|
git_lines_added: 100,
|
|
git_lines_deleted: 50,
|
|
};
|
|
|
|
const result = parseGitEvent(event, { maxDiffLength: 1000 });
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result).toHaveProperty('content');
|
|
expect(result).toHaveProperty('isTruncated', true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('parseGitEvents', () => {
|
|
it('should parse multiple git events from log events', () => {
|
|
const events: LogEvent[] = [
|
|
{
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git status',
|
|
git_type: 'status',
|
|
git_branch: 'main',
|
|
git_staged: [],
|
|
git_unstaged: [],
|
|
git_untracked: [],
|
|
},
|
|
{
|
|
ts: 1709337610000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Regular log',
|
|
},
|
|
{
|
|
ts: 1709337620000,
|
|
worker: 'w-test',
|
|
level: 'info',
|
|
msg: 'Git commit',
|
|
git_type: 'commit',
|
|
git_commit: 'abc123',
|
|
git_message: 'test commit',
|
|
},
|
|
];
|
|
|
|
const result = parseGitEvents(events);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0].type).toBe('status');
|
|
expect(result[1].type).toBe('commit');
|
|
});
|
|
});
|
|
|
|
describe('formatGitEvent', () => {
|
|
it('should format a git status event', () => {
|
|
const event: GitStatusEvent = {
|
|
id: 'ge-1',
|
|
type: 'status',
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
branch: 'main',
|
|
staged: [],
|
|
unstaged: [],
|
|
untracked: [],
|
|
};
|
|
|
|
const result = formatGitEvent(event);
|
|
|
|
expect(result).toContain('[git]');
|
|
expect(result).toContain('Status');
|
|
expect(result).toContain('Branch: main');
|
|
expect(result).toContain('Staged: 0 files');
|
|
});
|
|
|
|
it('should format a git commit event', () => {
|
|
const event: GitCommitEvent = {
|
|
id: 'ge-2',
|
|
type: 'commit',
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
hash: 'abc123def456',
|
|
message: 'feat: add new feature',
|
|
branch: 'main',
|
|
author: 'John Doe',
|
|
};
|
|
|
|
const result = formatGitEvent(event);
|
|
|
|
expect(result).toContain('[git]');
|
|
expect(result).toContain('Commit');
|
|
expect(result).toContain('abc123d'); // Short hash
|
|
expect(result).toContain('[main]');
|
|
expect(result).toContain('by John Doe');
|
|
expect(result).toContain('feat: add new feature');
|
|
});
|
|
|
|
it('should format a git branch event', () => {
|
|
const event = {
|
|
id: 'ge-3',
|
|
type: 'branch' as const,
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
current: 'feature/test',
|
|
tracking: 'origin/feature/test',
|
|
ahead: 2,
|
|
behind: 1,
|
|
};
|
|
|
|
const result = formatGitEvent(event);
|
|
|
|
expect(result).toContain('[git]');
|
|
expect(result).toContain('Branch');
|
|
expect(result).toContain('Current: feature/test');
|
|
expect(result).toContain('tracking origin/feature/test');
|
|
expect(result).toContain('+2');
|
|
expect(result).toContain('-1');
|
|
});
|
|
|
|
it('should format a git diff event', () => {
|
|
const event = {
|
|
id: 'ge-4',
|
|
type: 'diff' as const,
|
|
ts: 1709337600000,
|
|
worker: 'w-test',
|
|
target: 'origin/main',
|
|
files: [{ path: 'file1.ts', status: 'modified' as const, staged: false }],
|
|
linesAdded: 45,
|
|
linesDeleted: 12,
|
|
};
|
|
|
|
const result = formatGitEvent(event);
|
|
|
|
expect(result).toContain('[git]');
|
|
expect(result).toContain('Diff');
|
|
expect(result).toContain('Target: origin/main');
|
|
expect(result).toContain('+45/-12');
|
|
expect(result).toContain('1 files');
|
|
});
|
|
});
|