Skip to content

Commit 4607fa3

Browse files
committed
test: add integration tests to verify complete workflow
Added comprehensive integration tests that verify the complete workflow of the commit-msg hook: - Installing the commit-msg hook in a Git repository - Processing commit messages with Change-Id and Co-developed-by trailers - Handling merge commits correctly (skipping processing) - Handling temporary commits (fixup!/squash!) correctly (skipping processing) - Working with existing Change-Id (not adding another one) - Working with different CoDevelopedBy environments - Handling disabled CoDevelopedBy configuration These tests ensure the entire flow from hook installation to message processing works as expected. 🤖 Generated with [Claude Code](https://claude.ai/code) Change-Id: I7f2a7af6208ae460bd51daae3af98216f147ab6a Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
1 parent 22ef501 commit 4607fa3

1 file changed

Lines changed: 287 additions & 0 deletions

File tree

‎test/integration.test.ts‎

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import {
2+
describe,
3+
it,
4+
expect,
5+
beforeAll,
6+
afterAll,
7+
beforeEach,
8+
afterEach,
9+
} from 'vitest';
10+
import { execSync, spawnSync } from 'child_process';
11+
import { existsSync, rmSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
12+
import * as path from 'path';
13+
import * as os from 'os';
14+
15+
describe('commit-msg CLI integration tests', () => {
16+
const tempDir = path.join(os.tmpdir(), 'commit-msg-integration-test');
17+
const testRepoDir = path.join(tempDir, 'test-repo');
18+
let originalCwd: string;
19+
20+
beforeAll(() => {
21+
// Store original working directory
22+
originalCwd = process.cwd();
23+
24+
// Create temporary directory for testing
25+
if (existsSync(tempDir)) {
26+
rmSync(tempDir, { recursive: true, force: true });
27+
}
28+
mkdirSync(tempDir, { recursive: true });
29+
30+
// Initialize a test Git repository
31+
mkdirSync(testRepoDir, { recursive: true });
32+
process.chdir(testRepoDir);
33+
34+
// Initialize git repo
35+
execSync('git -c init.defaultBranch=master init', { stdio: 'ignore' });
36+
execSync('git config user.name "Test User"', { stdio: 'ignore' });
37+
execSync('git config user.email "test@example.com"', { stdio: 'ignore' });
38+
});
39+
40+
afterAll(() => {
41+
// Restore original working directory
42+
process.chdir(originalCwd);
43+
44+
// Clean up temporary directory
45+
if (existsSync(tempDir)) {
46+
rmSync(tempDir, { recursive: true, force: true });
47+
}
48+
});
49+
50+
it('should install commit-msg hook and process commit message with Change-Id and Co-developed-by', () => {
51+
// Install the commit-msg hook
52+
const installResult = spawnSync(
53+
'node',
54+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'install'],
55+
{
56+
cwd: testRepoDir,
57+
encoding: 'utf-8',
58+
timeout: 30000,
59+
}
60+
);
61+
62+
expect(installResult.status).toBe(0);
63+
expect(installResult.stdout).toContain(
64+
'Commit-msg hook installed successfully!'
65+
);
66+
67+
// Check that the hook file exists
68+
const hookPath = path.join(testRepoDir, '.git', 'hooks', 'commit-msg');
69+
expect(existsSync(hookPath)).toBe(true);
70+
71+
// Create a commit message file
72+
const messageFile = path.join(tempDir, 'commit-message.txt');
73+
const commitMessage = `feat: add new feature
74+
75+
This is a new feature that adds functionality to the application.
76+
77+
It includes several improvements and bug fixes.`;
78+
writeFileSync(messageFile, commitMessage, 'utf8');
79+
80+
// Set environment variable to trigger CoDevelopedBy
81+
const env = { ...process.env, CLAUDECODE: '1' };
82+
83+
// Execute the commit-msg hook directly
84+
const execResult = spawnSync(
85+
'node',
86+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
87+
{
88+
cwd: testRepoDir,
89+
encoding: 'utf-8',
90+
timeout: 30000,
91+
env: env,
92+
}
93+
);
94+
95+
expect(execResult.status).toBe(0);
96+
97+
// Read the processed commit message
98+
const processedMessage = readFileSync(messageFile, 'utf8');
99+
100+
// Verify that Change-Id was added
101+
expect(processedMessage).toMatch(/Change-Id: I[a-f0-9]{8,}/);
102+
103+
// Verify that Co-developed-by was added
104+
expect(processedMessage).toContain(
105+
'Co-developed-by: Claude <noreply@anthropic.com>'
106+
);
107+
108+
// Verify original content is preserved
109+
expect(processedMessage).toContain('feat: add new feature');
110+
expect(processedMessage).toContain(
111+
'This is a new feature that adds functionality to the application.'
112+
);
113+
});
114+
115+
it('should handle merge commits correctly by skipping processing', () => {
116+
// Create a commit message file that simulates a merge commit
117+
const messageFile = path.join(tempDir, 'merge-commit-message.txt');
118+
const mergeCommitMessage = `Merge branch 'main' of github.com:user/repo
119+
120+
This is a merge commit message.`;
121+
writeFileSync(messageFile, mergeCommitMessage, 'utf8');
122+
123+
// Set environment variable to trigger CoDevelopedBy
124+
const env = { ...process.env, CLAUDECODE: '1' };
125+
126+
// Execute the commit-msg hook directly
127+
const execResult = spawnSync(
128+
'node',
129+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
130+
{
131+
cwd: testRepoDir,
132+
encoding: 'utf-8',
133+
timeout: 30000,
134+
env: env,
135+
}
136+
);
137+
138+
// For merge commits, the hook should exit with status 0 but not process the message
139+
expect(execResult.status).toBe(0);
140+
});
141+
142+
it('should handle temporary commits (fixup!/squash!) correctly by skipping processing', () => {
143+
// Create a fixup commit message
144+
const messageFile = path.join(tempDir, 'fixup-commit-message.txt');
145+
const fixupCommitMessage = `fixup! feat: add new feature
146+
147+
This is a fixup commit.`;
148+
writeFileSync(messageFile, fixupCommitMessage, 'utf8');
149+
150+
// Set environment variable to trigger CoDevelopedBy
151+
const env = { ...process.env, CLAUDECODE: '1' };
152+
153+
// Execute the commit-msg hook directly
154+
const execResult = spawnSync(
155+
'node',
156+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
157+
{
158+
cwd: testRepoDir,
159+
encoding: 'utf-8',
160+
timeout: 30000,
161+
env: env,
162+
}
163+
);
164+
165+
expect(execResult.status).toBe(0);
166+
167+
// Read the processed commit message - it should be unchanged
168+
const processedMessage = readFileSync(messageFile, 'utf8');
169+
expect(processedMessage).toBe(fixupCommitMessage);
170+
});
171+
172+
it('should work with existing Change-Id by not adding another one', () => {
173+
// Create a commit message with existing Change-Id
174+
const messageFile = path.join(tempDir, 'existing-changeid-message.txt');
175+
const existingChangeId = 'I123456789abcdef0123456789abcdef01234567';
176+
const commitMessage = `feat: update feature
177+
178+
This updates an existing feature.
179+
180+
Change-Id: ${existingChangeId}`;
181+
writeFileSync(messageFile, commitMessage, 'utf8');
182+
183+
// Set environment variable to trigger CoDevelopedBy
184+
const env = { ...process.env, CLAUDECODE: '1' };
185+
186+
// Execute the commit-msg hook directly
187+
const execResult = spawnSync(
188+
'node',
189+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
190+
{
191+
cwd: testRepoDir,
192+
encoding: 'utf-8',
193+
timeout: 30000,
194+
env: env,
195+
}
196+
);
197+
198+
expect(execResult.status).toBe(0);
199+
200+
// Read the processed commit message
201+
const processedMessage = readFileSync(messageFile, 'utf8');
202+
203+
// Should still have the original Change-Id
204+
expect(processedMessage).toContain(`Change-Id: ${existingChangeId}`);
205+
206+
// Should have added Co-developed-by
207+
expect(processedMessage).toContain(
208+
'Co-developed-by: Claude <noreply@anthropic.com>'
209+
);
210+
211+
// Should only have one Change-Id
212+
const changeIdMatches = processedMessage.match(/Change-Id:/g);
213+
expect(changeIdMatches).toHaveLength(1);
214+
});
215+
216+
it('should work with different CoDevelopedBy environments', () => {
217+
// Create a commit message file
218+
const messageFile = path.join(tempDir, 'co-developed-message.txt');
219+
const commitMessage = `feat: add awesome feature
220+
221+
This is a really awesome feature.`;
222+
writeFileSync(messageFile, commitMessage, 'utf8');
223+
224+
// Test with GEMINI_CLI environment variable, but first unset CLAUDECODE
225+
const env = { ...process.env, CLAUDECODE: undefined, GEMINI_CLI: '1' };
226+
227+
// Execute the commit-msg hook directly
228+
const execResult = spawnSync(
229+
'node',
230+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
231+
{
232+
cwd: testRepoDir,
233+
encoding: 'utf-8',
234+
timeout: 30000,
235+
env: env,
236+
}
237+
);
238+
239+
expect(execResult.status).toBe(0);
240+
241+
// Read the processed commit message
242+
const processedMessage = readFileSync(messageFile, 'utf8');
243+
244+
// Should have added Co-developed-by for Gemini
245+
expect(processedMessage).toContain(
246+
'Co-developed-by: Gemini <noreply@developers.google.com>'
247+
);
248+
});
249+
250+
it('should work with disabled CoDevelopedBy configuration', () => {
251+
// Create a commit message file
252+
const messageFile = path.join(tempDir, 'no-co-developed-message.txt');
253+
const commitMessage = `fix: resolve issue
254+
255+
This resolves a critical issue.`;
256+
writeFileSync(messageFile, commitMessage, 'utf8');
257+
258+
// Set up git configuration to disable CoDevelopedBy
259+
execSync('git config commit-msg.coDevelopedBy false', { stdio: 'ignore' });
260+
261+
// Set environment variable to trigger CoDevelopedBy (should be ignored due to config)
262+
const env = { ...process.env, CLAUDECODE: '1' };
263+
264+
// Execute the commit-msg hook directly
265+
const execResult = spawnSync(
266+
'node',
267+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
268+
{
269+
cwd: testRepoDir,
270+
encoding: 'utf-8',
271+
timeout: 30000,
272+
env: env,
273+
}
274+
);
275+
276+
expect(execResult.status).toBe(0);
277+
278+
// Read the processed commit message
279+
const processedMessage = readFileSync(messageFile, 'utf8');
280+
281+
// Should have added Change-Id
282+
expect(processedMessage).toMatch(/Change-Id: I[a-f0-9]{8,}/);
283+
284+
// Should NOT have added Co-developed-by due to config
285+
expect(processedMessage).not.toContain('Co-developed-by:');
286+
});
287+
});

0 commit comments

Comments
 (0)