Skip to content

Commit 5c0e2fe

Browse files
Copiloteleanorjboyd
andcommitted
Add test script for CI failure issue creation logic
Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com>
1 parent 95d9427 commit 5c0e2fe

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/**
2+
* Test script for validating the CI failure issue creation logic
3+
* This simulates the GitHub Actions script to ensure it handles various scenarios correctly.
4+
*/
5+
6+
// Mock GitHub context
7+
const mockContext = {
8+
repo: {
9+
owner: 'microsoft',
10+
repo: 'vscode-python'
11+
}
12+
};
13+
14+
// Mock GitHub API
15+
const mockGitHub = {
16+
rest: {
17+
issues: {
18+
listForRepo: async ({ owner, repo, state, labels, per_page }) => {
19+
console.log(`✓ Called listForRepo with: owner=${owner}, repo=${repo}, state=${state}, labels=${labels}, per_page=${per_page}`);
20+
return {
21+
data: [
22+
// Simulate an existing recent issue
23+
{
24+
number: 12345,
25+
title: 'CI Failure on main: lint',
26+
created_at: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(), // 12 hours ago
27+
labels: [{ name: 'ci-failure' }]
28+
},
29+
// Simulate an old issue
30+
{
31+
number: 11111,
32+
title: 'CI Failure on main: tests',
33+
created_at: new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString(), // 48 hours ago
34+
labels: [{ name: 'ci-failure' }]
35+
}
36+
]
37+
};
38+
},
39+
create: async ({ owner, repo, title, body, labels }) => {
40+
console.log(`✓ Would create new issue:`);
41+
console.log(` Title: ${title}`);
42+
console.log(` Labels: ${labels.join(', ')}`);
43+
return { data: { number: 99999 } };
44+
},
45+
createComment: async ({ owner, repo, issue_number, body }) => {
46+
console.log(`✓ Would add comment to issue #${issue_number}`);
47+
return { data: {} };
48+
}
49+
}
50+
}
51+
};
52+
53+
// Test the logic from the workflow
54+
async function testIssueCreationLogic(scenarioName, jobResults, expectNewIssue) {
55+
console.log(`\n${'='.repeat(60)}`);
56+
console.log(`Testing: ${scenarioName}`);
57+
console.log('='.repeat(60));
58+
59+
const failedJobs = [];
60+
const jobs = {
61+
'build-vsix': jobResults.buildVsix || 'success',
62+
'lint': jobResults.lint || 'success',
63+
'check-types': jobResults.checkTypes || 'success',
64+
'python-tests': jobResults.pythonTests || 'success',
65+
'tests': jobResults.tests || 'success',
66+
'smoke-tests': jobResults.smokeTests || 'success'
67+
};
68+
69+
for (const [job, result] of Object.entries(jobs)) {
70+
if (result === 'failure') {
71+
failedJobs.push(job);
72+
}
73+
}
74+
75+
console.log(`Failed jobs: ${failedJobs.join(', ') || 'none'}`);
76+
77+
if (failedJobs.length === 0) {
78+
console.log('✓ No failures - workflow would not run');
79+
return;
80+
}
81+
82+
const title = `CI Failure on main: ${failedJobs.join(', ')}`;
83+
const body = `## CI Failure Report
84+
85+
The following jobs failed on the main branch:
86+
${failedJobs.map(job => `- **${job}**`).join('\n')}
87+
88+
**Workflow Run:** https://github.com/microsoft/vscode-python/actions/runs/123456789
89+
**Commit:** abc123def456
90+
**Commit Message:** Test commit
91+
**Author:** @testuser
92+
93+
Please investigate and fix the failure.
94+
95+
---
96+
*This issue was automatically created by the CI system.*`;
97+
98+
// Check for existing issues
99+
const existingIssues = await mockGitHub.rest.issues.listForRepo({
100+
owner: mockContext.repo.owner,
101+
repo: mockContext.repo.repo,
102+
state: 'open',
103+
labels: 'ci-failure',
104+
per_page: 100
105+
});
106+
107+
// Look for recent issues (within last 24 hours)
108+
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
109+
const recentIssue = existingIssues.data.find(issue => {
110+
const issueDate = new Date(issue.created_at);
111+
return issueDate > oneDayAgo && issue.title.includes('CI Failure on main');
112+
});
113+
114+
if (recentIssue) {
115+
await mockGitHub.rest.issues.createComment({
116+
owner: mockContext.repo.owner,
117+
repo: mockContext.repo.repo,
118+
issue_number: recentIssue.number,
119+
body: `## Additional CI Failure
120+
121+
Another CI failure occurred:
122+
${failedJobs.map(job => `- **${job}**`).join('\n')}
123+
124+
**Workflow Run:** https://github.com/microsoft/vscode-python/actions/runs/123456789
125+
**Commit:** abc123def456
126+
**Commit Message:** Test commit
127+
**Author:** @testuser`
128+
});
129+
console.log(`✓ Would comment on existing issue #${recentIssue.number} instead of creating new one`);
130+
if (expectNewIssue) {
131+
console.error('❌ FAILED: Expected new issue but would comment instead');
132+
} else {
133+
console.log('✓ PASSED: Correctly prevented duplicate issue');
134+
}
135+
} else {
136+
const issue = await mockGitHub.rest.issues.create({
137+
owner: mockContext.repo.owner,
138+
repo: mockContext.repo.repo,
139+
title: title,
140+
body: body,
141+
labels: ['ci-failure', 'bug', 'needs-triage']
142+
});
143+
console.log(`✓ Would create new issue #${issue.data.number}`);
144+
if (!expectNewIssue) {
145+
console.error('❌ FAILED: Created new issue but should have commented');
146+
} else {
147+
console.log('✓ PASSED: Correctly created new issue');
148+
}
149+
}
150+
}
151+
152+
// Run test scenarios
153+
(async () => {
154+
console.log('Starting CI Failure Issue Creation Tests...\n');
155+
156+
// Test 1: Single failure, recent issue exists
157+
await testIssueCreationLogic(
158+
'Single failure with recent issue (should comment)',
159+
{ lint: 'failure' },
160+
false // expect comment, not new issue
161+
);
162+
163+
// Test 2: Multiple failures, recent issue exists
164+
await testIssueCreationLogic(
165+
'Multiple failures with recent issue (should comment)',
166+
{ lint: 'failure', tests: 'failure' },
167+
false // expect comment, not new issue
168+
);
169+
170+
// Test 3: Failure with no recent issues (old issue exists but >24h)
171+
// Note: In the actual scenario, we filter for recent issues, so old ones don't count
172+
console.log('\n' + '='.repeat(60));
173+
console.log('Testing: Failure with no recent issues (should create new)');
174+
console.log('='.repeat(60));
175+
console.log('Modifying mock to return only old issues...');
176+
177+
const oldListForRepo = mockGitHub.rest.issues.listForRepo;
178+
mockGitHub.rest.issues.listForRepo = async (params) => {
179+
const result = await oldListForRepo(params);
180+
// Filter out recent issues for this test
181+
result.data = result.data.filter(issue => {
182+
const issueDate = new Date(issue.created_at);
183+
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
184+
return issueDate <= oneDayAgo;
185+
});
186+
return result;
187+
};
188+
189+
await testIssueCreationLogic(
190+
'Failure with only old issues (should create new)',
191+
{ pythonTests: 'failure' },
192+
true // expect new issue
193+
);
194+
195+
// Restore original mock
196+
mockGitHub.rest.issues.listForRepo = oldListForRepo;
197+
198+
console.log('\n' + '='.repeat(60));
199+
console.log('All tests completed!');
200+
console.log('='.repeat(60));
201+
})();

0 commit comments

Comments
 (0)