Skip to content

Commit c5b138d

Browse files
🧪 Add tests for git utilities (#38)
* 🧪 Add tests for git utilities Co-authored-by: sunnylqm <615282+sunnylqm@users.noreply.github.com> * 🧪 Fix tests for git utilities Co-authored-by: sunnylqm <615282+sunnylqm@users.noreply.github.com> --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: sunnylqm <615282+sunnylqm@users.noreply.github.com>
1 parent 192a37b commit c5b138d

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

‎src/utils/git.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { spawnSync } from 'child_process';
12
import fs from 'fs';
23
import git from 'isomorphic-git';
34
import path from 'path';
@@ -10,6 +11,14 @@ export interface CommitInfo {
1011
origin: string;
1112
}
1213

14+
export function getCurrentCommit() {
15+
const result = spawnSync('git', ['rev-parse', 'HEAD']);
16+
if (result.status !== 0) {
17+
throw new Error('Not a git repository');
18+
}
19+
return result.stdout.toString().trim();
20+
}
21+
1322
function findGitRoot(dir = process.cwd()) {
1423
const gitRoot = fs.readdirSync(dir).find((dir) => dir === '.git');
1524
if (gitRoot) {

‎tests/git.test.ts‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { describe, expect, it, mock } from 'bun:test';
2+
3+
// Define the mock functions so we can manipulate them in tests
4+
const spawnSyncMock = mock(() => ({
5+
status: 0,
6+
stdout: Buffer.from('mock-hash-123\n'),
7+
}));
8+
9+
mock.module('child_process', () => ({
10+
spawnSync: spawnSyncMock,
11+
}));
12+
13+
const listRemotesMock = mock(async () => [
14+
{ remote: 'origin', url: 'https://github.com/test/repo.git' },
15+
]);
16+
const logMock = mock(async () => [
17+
{
18+
oid: 'mock-commit-hash',
19+
commit: {
20+
message: 'mock commit message',
21+
author: { name: 'Test Author' },
22+
committer: { name: 'Test Committer', timestamp: 1625097600 },
23+
},
24+
},
25+
]);
26+
27+
mock.module('isomorphic-git', () => ({
28+
default: {
29+
listRemotes: listRemotesMock,
30+
log: logMock,
31+
},
32+
}));
33+
34+
describe('git utils', async () => {
35+
const { getCommitInfo, getCurrentCommit } = await import('../src/utils/git');
36+
37+
describe('getCurrentCommit', () => {
38+
it('should return the commit hash when git command succeeds', () => {
39+
spawnSyncMock.mockImplementationOnce(() => ({
40+
status: 0,
41+
stdout: Buffer.from('abcdef1234567890\n'),
42+
}));
43+
44+
const commit = getCurrentCommit();
45+
expect(commit).toBe('abcdef1234567890');
46+
expect(spawnSyncMock).toHaveBeenCalledWith('git', ['rev-parse', 'HEAD']);
47+
});
48+
49+
it('should throw an error when git command fails', () => {
50+
spawnSyncMock.mockImplementationOnce(() => ({
51+
status: 128,
52+
stdout: Buffer.from(''),
53+
stderr: Buffer.from(
54+
'fatal: not a git repository (or any of the parent directories): .git\n',
55+
),
56+
}));
57+
58+
expect(() => getCurrentCommit()).toThrow('Not a git repository');
59+
expect(spawnSyncMock).toHaveBeenCalledWith('git', ['rev-parse', 'HEAD']);
60+
});
61+
});
62+
63+
describe('getCommitInfo', () => {
64+
it('should return correct commit info', async () => {
65+
const info = await getCommitInfo();
66+
67+
expect(info).toBeDefined();
68+
expect(info?.hash).toBe('mock-commit-hash');
69+
expect(info?.message).toBe('mock commit message');
70+
expect(info?.author).toBe('Test Author');
71+
expect(info?.timestamp).toBe('1625097600');
72+
expect(info?.origin).toBe('https://github.com/test/repo.git');
73+
74+
expect(listRemotesMock).toHaveBeenCalled();
75+
expect(logMock).toHaveBeenCalled();
76+
});
77+
78+
it('should handle missing author name by falling back to committer name', async () => {
79+
logMock.mockImplementationOnce(async () => [
80+
{
81+
oid: 'mock-commit-hash-2',
82+
commit: {
83+
message: 'another message',
84+
author: { name: '' },
85+
committer: { name: 'Fallback Committer', timestamp: 1625098000 },
86+
},
87+
},
88+
]);
89+
90+
const info = await getCommitInfo();
91+
expect(info?.author).toBe('Fallback Committer');
92+
});
93+
94+
it('should return undefined and log error when git operations fail', async () => {
95+
const originalConsoleError = console.error;
96+
const consoleErrorMock = mock();
97+
console.error = consoleErrorMock;
98+
99+
listRemotesMock.mockImplementationOnce(async () => {
100+
throw new Error('Git operation failed');
101+
});
102+
103+
const info = await getCommitInfo();
104+
105+
expect(info).toBeUndefined();
106+
expect(consoleErrorMock).toHaveBeenCalled();
107+
108+
console.error = originalConsoleError;
109+
});
110+
});
111+
});

0 commit comments

Comments
 (0)