Skip to content

Commit fb0204d

Browse files
committed
fix tests
1 parent d33289f commit fb0204d

File tree

7 files changed

+240
-143
lines changed

7 files changed

+240
-143
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"devDependencies": {
2323
"@shelf/jest-mongodb": "^6.0.2",
2424
"@swc/core": "^1.3.0",
25-
"@types/jest": "^27.5.0",
25+
"@types/jest": "^29.5.0",
2626
"@types/xml2js": "^0.4.14",
2727
"eslint": "^6.7.2",
2828
"eslint-config-codex": "1.2.4",
@@ -31,7 +31,7 @@
3131
"mongodb-memory-server": "^6.6.1",
3232
"nodemon": "^2.0.2",
3333
"redis-mock": "^0.56.3",
34-
"ts-jest": "^27.1.0",
34+
"ts-jest": "^29.4.0",
3535
"ts-node": "^10.9.1",
3636
"typescript": "^4.7.4",
3737
"xml2js": "^0.6.2"

test/integrations/github.test.ts

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import '../../src/env-test';
2+
import { GitHubService } from '../../src/integrations/github/service';
3+
import jwt from 'jsonwebtoken';
24

35
/**
46
* Mock @octokit/rest as virtual mock since module might not be installed in test environment
@@ -13,14 +15,13 @@ jest.mock('@octokit/rest', () => ({
1315
*/
1416
jest.mock('jsonwebtoken');
1517

16-
import { GitHubService } from '../../src/integrations/github/service';
17-
import jwt from 'jsonwebtoken';
18-
1918
describe('GitHubService', () => {
2019
let githubService: GitHubService;
2120
const testAppId = '123456';
2221
const testAppSlug = 'hawk-tracker';
2322
const testPrivateKey = '-----BEGIN RSA PRIVATE KEY-----\nTEST_KEY\n-----END RSA PRIVATE KEY-----';
23+
const testClientId = 'Iv1.client-id';
24+
const testClientSecret = 'client-secret';
2425
const testInstallationId = '789012';
2526
const testApiUrl = 'https://api.example.com';
2627

@@ -35,13 +36,15 @@ describe('GitHubService', () => {
3536
addAssignees: jest.Mock;
3637
};
3738
};
39+
graphql: jest.Mock;
3840
};
3941

40-
const createMockOctokit = () => {
42+
const createMockOctokit = (): typeof mockOctokit => {
4143
const createTokenMock = jest.fn();
4244
const getInstallationMock = jest.fn();
4345
const createIssueMock = jest.fn();
4446
const addAssigneesMock = jest.fn();
47+
const graphqlMock = jest.fn();
4548

4649
return {
4750
rest: {
@@ -54,6 +57,7 @@ describe('GitHubService', () => {
5457
addAssignees: addAssigneesMock,
5558
},
5659
},
60+
graphql: graphqlMock,
5761
};
5862
};
5963

@@ -69,6 +73,8 @@ describe('GitHubService', () => {
6973
process.env.GITHUB_APP_ID = testAppId;
7074
process.env.GITHUB_APP_SLUG = testAppSlug;
7175
process.env.GITHUB_PRIVATE_KEY = testPrivateKey;
76+
process.env.GITHUB_APP_CLIENT_ID = testClientId;
77+
process.env.GITHUB_APP_CLIENT_SECRET = testClientSecret;
7278
process.env.API_URL = testApiUrl;
7379

7480
/**
@@ -79,8 +85,10 @@ describe('GitHubService', () => {
7985
/**
8086
* Get mocked Octokit constructor and set implementation
8187
*/
88+
// eslint-disable-next-line @typescript-eslint/no-var-requires
8289
const { Octokit } = require('@octokit/rest');
83-
(Octokit as jest.Mock).mockImplementation(() => mockOctokit);
90+
91+
Octokit.mockImplementation(() => mockOctokit);
8492

8593
/**
8694
* Create service instance
@@ -95,6 +103,8 @@ describe('GitHubService', () => {
95103
Reflect.deleteProperty(process.env, 'GITHUB_APP_ID');
96104
Reflect.deleteProperty(process.env, 'GITHUB_APP_SLUG');
97105
Reflect.deleteProperty(process.env, 'GITHUB_PRIVATE_KEY');
106+
Reflect.deleteProperty(process.env, 'GITHUB_APP_CLIENT_ID');
107+
Reflect.deleteProperty(process.env, 'GITHUB_APP_CLIENT_SECRET');
98108
Reflect.deleteProperty(process.env, 'API_URL');
99109
});
100110

@@ -105,7 +115,7 @@ describe('GitHubService', () => {
105115
const url = githubService.getInstallationUrl(state);
106116

107117
expect(url).toBe(
108-
`https://github.com/apps/${testAppSlug}/installations/new?state=${encodeURIComponent(state)}&redirect_url=${encodeURIComponent(`${testApiUrl}/integration/github/callback`)}`
118+
`https://github.com/apps/${testAppSlug}/installations/new?state=${encodeURIComponent(state)}&redirect_url=${encodeURIComponent(`${testApiUrl}/integration/github/oauth`)}`
109119
);
110120
});
111121

@@ -126,6 +136,7 @@ describe('GitHubService', () => {
126136
it('should get installation information for User account', async () => {
127137
(jwt.sign as jest.Mock).mockReturnValue(mockJwtToken);
128138

139+
/* eslint-disable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
129140
mockOctokit.rest.apps.getInstallation.mockResolvedValue({
130141
data: {
131142
id: 12345,
@@ -143,6 +154,7 @@ describe('GitHubService', () => {
143154
},
144155
},
145156
} as any);
157+
/* eslint-enable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
146158

147159
const result = await githubService.getInstallationForRepository(testInstallationId);
148160

@@ -152,6 +164,7 @@ describe('GitHubService', () => {
152164
login: 'octocat',
153165
type: 'User',
154166
},
167+
// eslint-disable-next-line @typescript-eslint/camelcase, camelcase
155168
target_type: 'User',
156169
permissions: {
157170
issues: 'write',
@@ -160,13 +173,15 @@ describe('GitHubService', () => {
160173
});
161174

162175
expect(mockOctokit.rest.apps.getInstallation).toHaveBeenCalledWith({
176+
// eslint-disable-next-line @typescript-eslint/camelcase, camelcase
163177
installation_id: parseInt(testInstallationId, 10),
164178
});
165179
});
166180

167181
it('should get installation information for Organization account', async () => {
168182
(jwt.sign as jest.Mock).mockReturnValue(mockJwtToken);
169183

184+
/* eslint-disable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
170185
mockOctokit.rest.apps.getInstallation.mockResolvedValue({
171186
data: {
172187
id: 12345,
@@ -184,6 +199,7 @@ describe('GitHubService', () => {
184199
},
185200
},
186201
} as any);
202+
/* eslint-enable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
187203

188204
const result = await githubService.getInstallationForRepository(testInstallationId);
189205

@@ -193,6 +209,7 @@ describe('GitHubService', () => {
193209
login: 'my-org',
194210
type: 'Organization',
195211
},
212+
// eslint-disable-next-line @typescript-eslint/camelcase, camelcase
196213
target_type: 'Organization',
197214
permissions: {
198215
issues: 'write',
@@ -219,21 +236,24 @@ describe('GitHubService', () => {
219236
beforeEach(() => {
220237
(jwt.sign as jest.Mock).mockReturnValue(mockJwtToken);
221238

239+
/* eslint-disable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
222240
mockOctokit.rest.apps.createInstallationAccessToken.mockResolvedValue({
223241
data: {
224242
token: mockInstallationToken,
225243
expires_at: '2025-01-01T00:00:00Z',
226244
},
227245
} as any);
246+
/* eslint-enable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
228247
});
229248

230249
it('should create issue successfully', async () => {
231250
const issueData = {
232251
title: 'Test Issue',
233252
body: 'Test body',
234-
labels: ['bug'],
253+
labels: [ 'bug' ],
235254
};
236255

256+
/* eslint-disable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
237257
mockOctokit.rest.issues.create.mockResolvedValue({
238258
data: {
239259
number: 123,
@@ -242,11 +262,13 @@ describe('GitHubService', () => {
242262
state: 'open',
243263
},
244264
} as any);
265+
/* eslint-enable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
245266

246267
const result = await githubService.createIssue('owner/repo', testInstallationId, issueData);
247268

248269
expect(result).toEqual({
249270
number: 123,
271+
// eslint-disable-next-line @typescript-eslint/camelcase, camelcase
250272
html_url: 'https://github.com/owner/repo/issues/123',
251273
title: 'Test Issue',
252274
state: 'open',
@@ -257,7 +279,7 @@ describe('GitHubService', () => {
257279
repo: 'repo',
258280
title: 'Test Issue',
259281
body: 'Test body',
260-
labels: ['bug'],
282+
labels: [ 'bug' ],
261283
});
262284
});
263285

@@ -267,6 +289,7 @@ describe('GitHubService', () => {
267289
body: 'Test body',
268290
};
269291

292+
/* eslint-disable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
270293
mockOctokit.rest.issues.create.mockResolvedValue({
271294
data: {
272295
number: 124,
@@ -275,6 +298,7 @@ describe('GitHubService', () => {
275298
state: 'open',
276299
},
277300
} as any);
301+
/* eslint-enable @typescript-eslint/camelcase, camelcase, @typescript-eslint/no-explicit-any */
278302

279303
const result = await githubService.createIssue('owner/repo', testInstallationId, issueData);
280304

@@ -314,45 +338,64 @@ describe('GitHubService', () => {
314338
});
315339

316340
describe('assignCopilot', () => {
317-
const mockJwtToken = 'mock-jwt-token';
318-
const mockInstallationToken = 'mock-installation-token';
319-
320-
beforeEach(() => {
321-
(jwt.sign as jest.Mock).mockReturnValue(mockJwtToken);
322-
323-
mockOctokit.rest.apps.createInstallationAccessToken.mockResolvedValue({
324-
data: {
325-
token: mockInstallationToken,
326-
expires_at: '2025-01-01T00:00:00Z',
327-
},
328-
} as any);
329-
});
341+
const mockDelegatedUserToken = 'mock-delegated-user-token';
330342

331343
it('should assign Copilot to issue successfully', async () => {
332344
const issueNumber = 123;
333345

334-
mockOctokit.rest.issues.addAssignees.mockResolvedValue({
335-
data: {},
336-
} as any);
346+
mockOctokit.graphql
347+
.mockResolvedValueOnce({
348+
repository: {
349+
id: 'repo-123',
350+
issue: { id: 'issue-456' },
351+
suggestedActors: {
352+
nodes: [
353+
{
354+
login: 'copilot-swe-agent',
355+
__typename: 'Bot',
356+
id: 'bot-789',
357+
},
358+
],
359+
},
360+
},
361+
})
362+
.mockResolvedValueOnce({
363+
addAssigneesToAssignable: {
364+
assignable: {
365+
id: 'issue-456',
366+
number: issueNumber,
367+
assignees: { nodes: [] },
368+
},
369+
},
370+
});
337371

338-
const result = await githubService.assignCopilot('owner', 'repo', issueNumber, testInstallationId);
372+
await githubService.assignCopilot('owner/repo', issueNumber, mockDelegatedUserToken);
339373

340-
expect(result).toBe(true);
341-
expect(mockOctokit.rest.issues.addAssignees).toHaveBeenCalledWith({
342-
owner: 'owner',
343-
repo: 'repo',
344-
issue_number: issueNumber,
345-
assignees: ['github-copilot[bot]'],
346-
});
374+
expect(mockOctokit.graphql).toHaveBeenCalledTimes(2);
347375
});
348376

349377
it('should throw error if assignment fails', async () => {
350378
const issueNumber = 123;
351379

352-
mockOctokit.rest.issues.addAssignees.mockRejectedValue(new Error('Issue not found'));
380+
mockOctokit.graphql
381+
.mockResolvedValueOnce({
382+
repository: {
383+
id: 'repo-123',
384+
issue: { id: 'issue-456' },
385+
suggestedActors: {
386+
nodes: [
387+
{
388+
login: 'copilot-swe-agent',
389+
id: 'bot-789',
390+
},
391+
],
392+
},
393+
},
394+
})
395+
.mockRejectedValue(new Error('Issue not found'));
353396

354397
await expect(
355-
githubService.assignCopilot('owner', 'repo', issueNumber, testInstallationId)
398+
githubService.assignCopilot('owner/repo', issueNumber, mockDelegatedUserToken)
356399
).rejects.toThrow('Failed to assign Copilot');
357400
});
358401
});

test/models/businessOperation.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ describe('Business operation model', () => {
6060
});
6161
});
6262

63-
afterAll(async done => {
63+
afterAll(async () => {
6464
await mongo.mongoClients.hawk?.close();
6565
await mongo.mongoClients.events?.close();
66-
67-
done();
6866
});

test/models/businessOperationsFactory.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ describe('Business operation factory', () => {
4343

4444
});
4545

46-
afterAll(async done => {
46+
afterAll(async () => {
4747
await mongo.mongoClients.hawk?.close();
4848
await mongo.mongoClients.events?.close();
49-
50-
done();
5149
});
5250

test/models/user.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,8 @@ describe('UserModel SSO identities', () => {
161161

162162
});
163163

164-
afterAll(async done => {
164+
afterAll(async () => {
165165
await mongo.mongoClients.hawk?.close();
166166
await mongo.mongoClients.events?.close();
167-
168-
done();
169167
});
170168

test/models/usersFactory.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,8 @@ describe('UsersFactory SSO identities', () => {
130130
});
131131
});
132132

133-
afterAll(async done => {
133+
afterAll(async () => {
134134
await mongo.mongoClients.hawk?.close();
135135
await mongo.mongoClients.events?.close();
136-
137-
done();
138136
});
139137

0 commit comments

Comments
 (0)