@atlassian-dc-mcp/bitbucket 0.9.8 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/build/__tests__/bitbucket-service.test.js +701 -1
- package/build/__tests__/bitbucket-service.test.js.map +1 -1
- package/build/bitbucket-service.d.ts +114 -0
- package/build/bitbucket-service.d.ts.map +1 -1
- package/build/bitbucket-service.js +162 -0
- package/build/bitbucket-service.js.map +1 -1
- package/build/index.js +20 -0
- package/build/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/bitbucket-service.test.ts +1136 -1
- package/src/bitbucket-service.ts +248 -0
- package/src/index.ts +50 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -9,7 +9,12 @@ jest.mock('../bitbucket-client/index.js', () => ({
|
|
|
9
9
|
PullRequestsService: {
|
|
10
10
|
streamRawDiff2: jest.fn(),
|
|
11
11
|
createComment2: jest.fn(),
|
|
12
|
-
streamChanges1: jest.fn()
|
|
12
|
+
streamChanges1: jest.fn(),
|
|
13
|
+
create: jest.fn(),
|
|
14
|
+
update: jest.fn(),
|
|
15
|
+
getPage: jest.fn(),
|
|
16
|
+
getReviewers: jest.fn(),
|
|
17
|
+
get3: jest.fn()
|
|
13
18
|
},
|
|
14
19
|
OpenAPI: {
|
|
15
20
|
BASE: '',
|
|
@@ -63,6 +68,366 @@ describe('BitbucketService', () => {
|
|
|
63
68
|
expect(result.error).toBe('API Error');
|
|
64
69
|
});
|
|
65
70
|
});
|
|
71
|
+
describe('getPullRequests', () => {
|
|
72
|
+
it('should successfully get pull requests with default parameters', async () => {
|
|
73
|
+
const mockPullRequestsData = {
|
|
74
|
+
values: [
|
|
75
|
+
{
|
|
76
|
+
id: 1,
|
|
77
|
+
title: 'Test PR 1',
|
|
78
|
+
state: 'OPEN',
|
|
79
|
+
author: { user: { name: 'user1' } }
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 2,
|
|
83
|
+
title: 'Test PR 2',
|
|
84
|
+
state: 'OPEN',
|
|
85
|
+
author: { user: { name: 'user2' } }
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
size: 2,
|
|
89
|
+
isLastPage: true,
|
|
90
|
+
start: 0,
|
|
91
|
+
limit: 25
|
|
92
|
+
};
|
|
93
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
94
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug);
|
|
95
|
+
expect(result.success).toBe(true);
|
|
96
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
97
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
98
|
+
undefined, // at
|
|
99
|
+
undefined, // withProperties
|
|
100
|
+
undefined, // draft
|
|
101
|
+
undefined, // filterText
|
|
102
|
+
undefined, // state
|
|
103
|
+
undefined, // order
|
|
104
|
+
undefined, // direction
|
|
105
|
+
undefined, // start
|
|
106
|
+
25 // limit
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
it('should successfully get pull requests with state filter', async () => {
|
|
110
|
+
const mockPullRequestsData = {
|
|
111
|
+
values: [
|
|
112
|
+
{
|
|
113
|
+
id: 1,
|
|
114
|
+
title: 'Merged PR',
|
|
115
|
+
state: 'MERGED',
|
|
116
|
+
author: { user: { name: 'user1' } }
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
size: 1,
|
|
120
|
+
isLastPage: true,
|
|
121
|
+
start: 0,
|
|
122
|
+
limit: 25
|
|
123
|
+
};
|
|
124
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
125
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
126
|
+
undefined, // at
|
|
127
|
+
undefined, // withProperties
|
|
128
|
+
undefined, // draft
|
|
129
|
+
undefined, // filterText
|
|
130
|
+
'MERGED' // state
|
|
131
|
+
);
|
|
132
|
+
expect(result.success).toBe(true);
|
|
133
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
134
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, undefined, 'MERGED', undefined, undefined, undefined, 25);
|
|
135
|
+
});
|
|
136
|
+
it('should successfully get pull requests with text filter', async () => {
|
|
137
|
+
const mockPullRequestsData = {
|
|
138
|
+
values: [
|
|
139
|
+
{
|
|
140
|
+
id: 1,
|
|
141
|
+
title: 'Fix bug in authentication',
|
|
142
|
+
state: 'OPEN',
|
|
143
|
+
author: { user: { name: 'user1' } }
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
size: 1,
|
|
147
|
+
isLastPage: true,
|
|
148
|
+
start: 0,
|
|
149
|
+
limit: 25
|
|
150
|
+
};
|
|
151
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
152
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
153
|
+
undefined, // at
|
|
154
|
+
undefined, // withProperties
|
|
155
|
+
undefined, // draft
|
|
156
|
+
'authentication' // filterText
|
|
157
|
+
);
|
|
158
|
+
expect(result.success).toBe(true);
|
|
159
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
160
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, 'authentication', undefined, undefined, undefined, undefined, 25);
|
|
161
|
+
});
|
|
162
|
+
it('should successfully get pull requests with branch filter', async () => {
|
|
163
|
+
const mockPullRequestsData = {
|
|
164
|
+
values: [
|
|
165
|
+
{
|
|
166
|
+
id: 1,
|
|
167
|
+
title: 'PR to master',
|
|
168
|
+
state: 'OPEN',
|
|
169
|
+
toRef: { id: 'refs/heads/master' }
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
size: 1,
|
|
173
|
+
isLastPage: true,
|
|
174
|
+
start: 0,
|
|
175
|
+
limit: 25
|
|
176
|
+
};
|
|
177
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
178
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
179
|
+
'refs/heads/master' // at
|
|
180
|
+
);
|
|
181
|
+
expect(result.success).toBe(true);
|
|
182
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
183
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, 'refs/heads/master', undefined, undefined, undefined, undefined, undefined, undefined, undefined, 25);
|
|
184
|
+
});
|
|
185
|
+
it('should successfully get pull requests with direction filter', async () => {
|
|
186
|
+
const mockPullRequestsData = {
|
|
187
|
+
values: [
|
|
188
|
+
{
|
|
189
|
+
id: 1,
|
|
190
|
+
title: 'Outgoing PR',
|
|
191
|
+
state: 'OPEN'
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
size: 1,
|
|
195
|
+
isLastPage: true,
|
|
196
|
+
start: 0,
|
|
197
|
+
limit: 25
|
|
198
|
+
};
|
|
199
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
200
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
201
|
+
undefined, // at
|
|
202
|
+
undefined, // withProperties
|
|
203
|
+
undefined, // draft
|
|
204
|
+
undefined, // filterText
|
|
205
|
+
undefined, // state
|
|
206
|
+
undefined, // order
|
|
207
|
+
'OUTGOING' // direction
|
|
208
|
+
);
|
|
209
|
+
expect(result.success).toBe(true);
|
|
210
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
211
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'OUTGOING', undefined, 25);
|
|
212
|
+
});
|
|
213
|
+
it('should successfully get pull requests with order filter', async () => {
|
|
214
|
+
const mockPullRequestsData = {
|
|
215
|
+
values: [
|
|
216
|
+
{
|
|
217
|
+
id: 1,
|
|
218
|
+
title: 'Oldest PR',
|
|
219
|
+
state: 'OPEN',
|
|
220
|
+
createdDate: 1234567890
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 2,
|
|
224
|
+
title: 'Newer PR',
|
|
225
|
+
state: 'OPEN',
|
|
226
|
+
createdDate: 1234567900
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
size: 2,
|
|
230
|
+
isLastPage: true,
|
|
231
|
+
start: 0,
|
|
232
|
+
limit: 25
|
|
233
|
+
};
|
|
234
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
235
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
236
|
+
undefined, // at
|
|
237
|
+
undefined, // withProperties
|
|
238
|
+
undefined, // draft
|
|
239
|
+
undefined, // filterText
|
|
240
|
+
undefined, // state
|
|
241
|
+
'OLDEST' // order
|
|
242
|
+
);
|
|
243
|
+
expect(result.success).toBe(true);
|
|
244
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
245
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, undefined, undefined, 'OLDEST', undefined, undefined, 25);
|
|
246
|
+
});
|
|
247
|
+
it('should successfully get pull requests with draft filter', async () => {
|
|
248
|
+
const mockPullRequestsData = {
|
|
249
|
+
values: [
|
|
250
|
+
{
|
|
251
|
+
id: 1,
|
|
252
|
+
title: 'Draft PR',
|
|
253
|
+
state: 'OPEN',
|
|
254
|
+
draft: true
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
size: 1,
|
|
258
|
+
isLastPage: true,
|
|
259
|
+
start: 0,
|
|
260
|
+
limit: 25
|
|
261
|
+
};
|
|
262
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
263
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
264
|
+
undefined, // at
|
|
265
|
+
undefined, // withProperties
|
|
266
|
+
'true' // draft
|
|
267
|
+
);
|
|
268
|
+
expect(result.success).toBe(true);
|
|
269
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
270
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, 'true', undefined, undefined, undefined, undefined, undefined, 25);
|
|
271
|
+
});
|
|
272
|
+
it('should successfully get pull requests with pagination', async () => {
|
|
273
|
+
const mockPullRequestsData = {
|
|
274
|
+
values: [
|
|
275
|
+
{
|
|
276
|
+
id: 51,
|
|
277
|
+
title: 'PR 51',
|
|
278
|
+
state: 'OPEN'
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
size: 1,
|
|
282
|
+
isLastPage: false,
|
|
283
|
+
start: 50,
|
|
284
|
+
limit: 10,
|
|
285
|
+
nextPageStart: 60
|
|
286
|
+
};
|
|
287
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
288
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, // withAttributes
|
|
289
|
+
undefined, // at
|
|
290
|
+
undefined, // withProperties
|
|
291
|
+
undefined, // draft
|
|
292
|
+
undefined, // filterText
|
|
293
|
+
undefined, // state
|
|
294
|
+
undefined, // order
|
|
295
|
+
undefined, // direction
|
|
296
|
+
50, // start
|
|
297
|
+
10 // limit
|
|
298
|
+
);
|
|
299
|
+
expect(result.success).toBe(true);
|
|
300
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
301
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 50, 10);
|
|
302
|
+
});
|
|
303
|
+
it('should successfully get pull requests with all parameters', async () => {
|
|
304
|
+
const mockPullRequestsData = {
|
|
305
|
+
values: [
|
|
306
|
+
{
|
|
307
|
+
id: 1,
|
|
308
|
+
title: 'Complete PR test',
|
|
309
|
+
state: 'OPEN',
|
|
310
|
+
draft: false
|
|
311
|
+
}
|
|
312
|
+
],
|
|
313
|
+
size: 1,
|
|
314
|
+
isLastPage: true,
|
|
315
|
+
start: 0,
|
|
316
|
+
limit: 50
|
|
317
|
+
};
|
|
318
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
319
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, 'true', // withAttributes
|
|
320
|
+
'refs/heads/develop', // at
|
|
321
|
+
'true', // withProperties
|
|
322
|
+
'false', // draft
|
|
323
|
+
'test', // filterText
|
|
324
|
+
'ALL', // state
|
|
325
|
+
'NEWEST', // order
|
|
326
|
+
'INCOMING', // direction
|
|
327
|
+
0, // start
|
|
328
|
+
50 // limit
|
|
329
|
+
);
|
|
330
|
+
expect(result.success).toBe(true);
|
|
331
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
332
|
+
expect(PullRequestsService.getPage).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, 'true', 'refs/heads/develop', 'true', 'false', 'test', 'ALL', 'NEWEST', 'INCOMING', 0, 50);
|
|
333
|
+
});
|
|
334
|
+
it('should handle empty results', async () => {
|
|
335
|
+
const mockPullRequestsData = {
|
|
336
|
+
values: [],
|
|
337
|
+
size: 0,
|
|
338
|
+
isLastPage: true,
|
|
339
|
+
start: 0,
|
|
340
|
+
limit: 25
|
|
341
|
+
};
|
|
342
|
+
PullRequestsService.getPage.mockResolvedValue(mockPullRequestsData);
|
|
343
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug);
|
|
344
|
+
expect(result.success).toBe(true);
|
|
345
|
+
expect(result.data).toBe(mockPullRequestsData);
|
|
346
|
+
if (result.data) {
|
|
347
|
+
expect(result.data.values).toHaveLength(0);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
it('should handle API errors gracefully', async () => {
|
|
351
|
+
const mockError = new Error('Failed to fetch pull requests');
|
|
352
|
+
PullRequestsService.getPage.mockRejectedValue(mockError);
|
|
353
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug);
|
|
354
|
+
expect(result.success).toBe(false);
|
|
355
|
+
expect(result.error).toBe('Failed to fetch pull requests');
|
|
356
|
+
});
|
|
357
|
+
it('should handle permission errors', async () => {
|
|
358
|
+
const mockError = new Error('Insufficient permissions');
|
|
359
|
+
PullRequestsService.getPage.mockRejectedValue(mockError);
|
|
360
|
+
const result = await bitbucketService.getPullRequests(mockProjectKey, mockRepositorySlug, undefined, undefined, undefined, undefined, undefined, 'OPEN');
|
|
361
|
+
expect(result.success).toBe(false);
|
|
362
|
+
expect(result.error).toBe('Insufficient permissions');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
describe('getPullRequest', () => {
|
|
366
|
+
it('should successfully get a specific pull request by ID', async () => {
|
|
367
|
+
const mockPullRequestData = {
|
|
368
|
+
id: 123,
|
|
369
|
+
version: 1,
|
|
370
|
+
title: 'Feature: Add new functionality',
|
|
371
|
+
description: 'This PR adds new functionality',
|
|
372
|
+
state: 'OPEN',
|
|
373
|
+
open: true,
|
|
374
|
+
closed: false,
|
|
375
|
+
createdDate: 1234567890,
|
|
376
|
+
updatedDate: 1234567900,
|
|
377
|
+
fromRef: {
|
|
378
|
+
id: 'refs/heads/feature-branch',
|
|
379
|
+
displayId: 'feature-branch',
|
|
380
|
+
latestCommit: 'abc123',
|
|
381
|
+
repository: {
|
|
382
|
+
slug: 'test-repo',
|
|
383
|
+
project: { key: 'TEST' }
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
toRef: {
|
|
387
|
+
id: 'refs/heads/main',
|
|
388
|
+
displayId: 'main',
|
|
389
|
+
latestCommit: 'def456',
|
|
390
|
+
repository: {
|
|
391
|
+
slug: 'test-repo',
|
|
392
|
+
project: { key: 'TEST' }
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
author: {
|
|
396
|
+
user: {
|
|
397
|
+
name: 'testuser',
|
|
398
|
+
emailAddress: 'test@example.com',
|
|
399
|
+
displayName: 'Test User'
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
reviewers: [
|
|
403
|
+
{
|
|
404
|
+
user: { name: 'reviewer1', displayName: 'Reviewer One' },
|
|
405
|
+
approved: true
|
|
406
|
+
}
|
|
407
|
+
],
|
|
408
|
+
participants: []
|
|
409
|
+
};
|
|
410
|
+
PullRequestsService.get3.mockResolvedValue(mockPullRequestData);
|
|
411
|
+
const result = await bitbucketService.getPullRequest(mockProjectKey, mockRepositorySlug, '123');
|
|
412
|
+
expect(result.success).toBe(true);
|
|
413
|
+
expect(result.data).toBe(mockPullRequestData);
|
|
414
|
+
expect(PullRequestsService.get3).toHaveBeenCalledWith(mockProjectKey, '123', mockRepositorySlug);
|
|
415
|
+
});
|
|
416
|
+
it('should handle errors when pull request does not exist', async () => {
|
|
417
|
+
const mockError = new Error('Not found');
|
|
418
|
+
PullRequestsService.get3.mockRejectedValue(mockError);
|
|
419
|
+
const result = await bitbucketService.getPullRequest(mockProjectKey, mockRepositorySlug, '999');
|
|
420
|
+
expect(result.success).toBe(false);
|
|
421
|
+
expect(result.error).toBe('Not found');
|
|
422
|
+
});
|
|
423
|
+
it('should handle permission errors', async () => {
|
|
424
|
+
const mockError = new Error('Insufficient permissions');
|
|
425
|
+
PullRequestsService.get3.mockRejectedValue(mockError);
|
|
426
|
+
const result = await bitbucketService.getPullRequest(mockProjectKey, mockRepositorySlug, '123');
|
|
427
|
+
expect(result.success).toBe(false);
|
|
428
|
+
expect(result.error).toBe('Insufficient permissions');
|
|
429
|
+
});
|
|
430
|
+
});
|
|
66
431
|
describe('postPullRequestComment', () => {
|
|
67
432
|
it('should successfully post a general PR comment', async () => {
|
|
68
433
|
const mockComment = {
|
|
@@ -229,6 +594,341 @@ describe('BitbucketService', () => {
|
|
|
229
594
|
expect(result.error).toBe('API Error');
|
|
230
595
|
});
|
|
231
596
|
});
|
|
597
|
+
describe('createPullRequest', () => {
|
|
598
|
+
it('should successfully create a PR with minimal parameters', async () => {
|
|
599
|
+
const mockPullRequest = {
|
|
600
|
+
id: 1,
|
|
601
|
+
version: 0,
|
|
602
|
+
title: 'Test PR',
|
|
603
|
+
description: 'Test description',
|
|
604
|
+
state: 'OPEN',
|
|
605
|
+
fromRef: {
|
|
606
|
+
id: 'refs/heads/feature-branch',
|
|
607
|
+
repository: {
|
|
608
|
+
slug: mockRepositorySlug,
|
|
609
|
+
project: { key: mockProjectKey }
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
toRef: {
|
|
613
|
+
id: 'refs/heads/main',
|
|
614
|
+
repository: {
|
|
615
|
+
slug: mockRepositorySlug,
|
|
616
|
+
project: { key: mockProjectKey }
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
PullRequestsService.create.mockResolvedValue(mockPullRequest);
|
|
621
|
+
const result = await bitbucketService.createPullRequest(mockProjectKey, mockRepositorySlug, 'Test PR', 'Test description', 'refs/heads/feature-branch', 'refs/heads/main');
|
|
622
|
+
expect(result.success).toBe(true);
|
|
623
|
+
expect(result.data).toBe(mockPullRequest);
|
|
624
|
+
expect(PullRequestsService.create).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, {
|
|
625
|
+
title: 'Test PR',
|
|
626
|
+
description: 'Test description',
|
|
627
|
+
fromRef: {
|
|
628
|
+
id: 'refs/heads/feature-branch',
|
|
629
|
+
repository: {
|
|
630
|
+
slug: mockRepositorySlug,
|
|
631
|
+
project: { key: mockProjectKey }
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
toRef: {
|
|
635
|
+
id: 'refs/heads/main',
|
|
636
|
+
repository: {
|
|
637
|
+
slug: mockRepositorySlug,
|
|
638
|
+
project: { key: mockProjectKey }
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
it('should successfully create a PR without description', async () => {
|
|
644
|
+
const mockPullRequest = {
|
|
645
|
+
id: 2,
|
|
646
|
+
version: 0,
|
|
647
|
+
title: 'Test PR without description',
|
|
648
|
+
state: 'OPEN',
|
|
649
|
+
fromRef: {
|
|
650
|
+
id: 'refs/heads/feature-branch',
|
|
651
|
+
repository: {
|
|
652
|
+
slug: mockRepositorySlug,
|
|
653
|
+
project: { key: mockProjectKey }
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
toRef: {
|
|
657
|
+
id: 'refs/heads/main',
|
|
658
|
+
repository: {
|
|
659
|
+
slug: mockRepositorySlug,
|
|
660
|
+
project: { key: mockProjectKey }
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
PullRequestsService.create.mockResolvedValue(mockPullRequest);
|
|
665
|
+
const result = await bitbucketService.createPullRequest(mockProjectKey, mockRepositorySlug, 'Test PR without description', undefined, 'refs/heads/feature-branch', 'refs/heads/main');
|
|
666
|
+
expect(result.success).toBe(true);
|
|
667
|
+
expect(result.data).toBe(mockPullRequest);
|
|
668
|
+
expect(PullRequestsService.create).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, expect.objectContaining({
|
|
669
|
+
title: 'Test PR without description',
|
|
670
|
+
description: undefined
|
|
671
|
+
}));
|
|
672
|
+
});
|
|
673
|
+
it('should successfully create a PR with reviewers', async () => {
|
|
674
|
+
const mockPullRequest = {
|
|
675
|
+
id: 3,
|
|
676
|
+
version: 0,
|
|
677
|
+
title: 'Test PR with reviewers',
|
|
678
|
+
description: 'PR with reviewers',
|
|
679
|
+
state: 'OPEN',
|
|
680
|
+
fromRef: {
|
|
681
|
+
id: 'refs/heads/feature-branch',
|
|
682
|
+
repository: {
|
|
683
|
+
slug: mockRepositorySlug,
|
|
684
|
+
project: { key: mockProjectKey }
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
toRef: {
|
|
688
|
+
id: 'refs/heads/main',
|
|
689
|
+
repository: {
|
|
690
|
+
slug: mockRepositorySlug,
|
|
691
|
+
project: { key: mockProjectKey }
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
reviewers: [
|
|
695
|
+
{ user: { name: 'reviewer1' } },
|
|
696
|
+
{ user: { name: 'reviewer2' } }
|
|
697
|
+
]
|
|
698
|
+
};
|
|
699
|
+
PullRequestsService.create.mockResolvedValue(mockPullRequest);
|
|
700
|
+
const result = await bitbucketService.createPullRequest(mockProjectKey, mockRepositorySlug, 'Test PR with reviewers', 'PR with reviewers', 'refs/heads/feature-branch', 'refs/heads/main', ['reviewer1', 'reviewer2']);
|
|
701
|
+
expect(result.success).toBe(true);
|
|
702
|
+
expect(result.data).toBe(mockPullRequest);
|
|
703
|
+
expect(PullRequestsService.create).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, expect.objectContaining({
|
|
704
|
+
title: 'Test PR with reviewers',
|
|
705
|
+
reviewers: [
|
|
706
|
+
{ user: { name: 'reviewer1' } },
|
|
707
|
+
{ user: { name: 'reviewer2' } }
|
|
708
|
+
]
|
|
709
|
+
}));
|
|
710
|
+
});
|
|
711
|
+
it('should successfully create a PR with empty reviewers array', async () => {
|
|
712
|
+
const mockPullRequest = {
|
|
713
|
+
id: 4,
|
|
714
|
+
version: 0,
|
|
715
|
+
title: 'Test PR',
|
|
716
|
+
description: 'Test',
|
|
717
|
+
state: 'OPEN'
|
|
718
|
+
};
|
|
719
|
+
PullRequestsService.create.mockResolvedValue(mockPullRequest);
|
|
720
|
+
const result = await bitbucketService.createPullRequest(mockProjectKey, mockRepositorySlug, 'Test PR', 'Test', 'refs/heads/feature-branch', 'refs/heads/main', []);
|
|
721
|
+
expect(result.success).toBe(true);
|
|
722
|
+
expect(PullRequestsService.create).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, expect.not.objectContaining({
|
|
723
|
+
reviewers: expect.anything()
|
|
724
|
+
}));
|
|
725
|
+
});
|
|
726
|
+
it('should handle API errors gracefully', async () => {
|
|
727
|
+
const mockError = new Error('Failed to create PR');
|
|
728
|
+
PullRequestsService.create.mockRejectedValue(mockError);
|
|
729
|
+
const result = await bitbucketService.createPullRequest(mockProjectKey, mockRepositorySlug, 'Test PR', 'Test description', 'refs/heads/feature-branch', 'refs/heads/main');
|
|
730
|
+
expect(result.success).toBe(false);
|
|
731
|
+
expect(result.error).toBe('Failed to create PR');
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
describe('updatePullRequest', () => {
|
|
735
|
+
it('should successfully update PR with only title', async () => {
|
|
736
|
+
const mockUpdatedPR = {
|
|
737
|
+
id: 1,
|
|
738
|
+
version: 1,
|
|
739
|
+
title: 'Updated Title',
|
|
740
|
+
description: 'Original description',
|
|
741
|
+
state: 'OPEN'
|
|
742
|
+
};
|
|
743
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
744
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, 'Updated Title');
|
|
745
|
+
expect(result.success).toBe(true);
|
|
746
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
747
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
748
|
+
version: 0,
|
|
749
|
+
title: 'Updated Title'
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
it('should successfully update PR with only description', async () => {
|
|
753
|
+
const mockUpdatedPR = {
|
|
754
|
+
id: 1,
|
|
755
|
+
version: 1,
|
|
756
|
+
title: 'Original Title',
|
|
757
|
+
description: 'Updated description',
|
|
758
|
+
state: 'OPEN'
|
|
759
|
+
};
|
|
760
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
761
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, undefined, 'Updated description');
|
|
762
|
+
expect(result.success).toBe(true);
|
|
763
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
764
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
765
|
+
version: 0,
|
|
766
|
+
description: 'Updated description'
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
it('should successfully update PR with title and description', async () => {
|
|
770
|
+
const mockUpdatedPR = {
|
|
771
|
+
id: 1,
|
|
772
|
+
version: 1,
|
|
773
|
+
title: 'Updated Title',
|
|
774
|
+
description: 'Updated description',
|
|
775
|
+
state: 'OPEN'
|
|
776
|
+
};
|
|
777
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
778
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, 'Updated Title', 'Updated description');
|
|
779
|
+
expect(result.success).toBe(true);
|
|
780
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
781
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
782
|
+
version: 0,
|
|
783
|
+
title: 'Updated Title',
|
|
784
|
+
description: 'Updated description'
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
it('should successfully update PR with reviewers', async () => {
|
|
788
|
+
const mockUpdatedPR = {
|
|
789
|
+
id: 1,
|
|
790
|
+
version: 1,
|
|
791
|
+
title: 'Test PR',
|
|
792
|
+
description: 'Test',
|
|
793
|
+
state: 'OPEN',
|
|
794
|
+
reviewers: [
|
|
795
|
+
{ user: { name: 'reviewer1' } },
|
|
796
|
+
{ user: { name: 'reviewer2' } },
|
|
797
|
+
{ user: { name: 'reviewer3' } }
|
|
798
|
+
]
|
|
799
|
+
};
|
|
800
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
801
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, undefined, undefined, ['reviewer1', 'reviewer2', 'reviewer3']);
|
|
802
|
+
expect(result.success).toBe(true);
|
|
803
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
804
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
805
|
+
version: 0,
|
|
806
|
+
reviewers: [
|
|
807
|
+
{ user: { name: 'reviewer1' } },
|
|
808
|
+
{ user: { name: 'reviewer2' } },
|
|
809
|
+
{ user: { name: 'reviewer3' } }
|
|
810
|
+
]
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
it('should successfully update PR with all parameters', async () => {
|
|
814
|
+
const mockUpdatedPR = {
|
|
815
|
+
id: 1,
|
|
816
|
+
version: 2,
|
|
817
|
+
title: 'Updated Title',
|
|
818
|
+
description: 'Updated description',
|
|
819
|
+
state: 'OPEN',
|
|
820
|
+
reviewers: [
|
|
821
|
+
{ user: { name: 'newreviewer1' } },
|
|
822
|
+
{ user: { name: 'newreviewer2' } }
|
|
823
|
+
]
|
|
824
|
+
};
|
|
825
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
826
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 1, 'Updated Title', 'Updated description', ['newreviewer1', 'newreviewer2']);
|
|
827
|
+
expect(result.success).toBe(true);
|
|
828
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
829
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
830
|
+
version: 1,
|
|
831
|
+
title: 'Updated Title',
|
|
832
|
+
description: 'Updated description',
|
|
833
|
+
reviewers: [
|
|
834
|
+
{ user: { name: 'newreviewer1' } },
|
|
835
|
+
{ user: { name: 'newreviewer2' } }
|
|
836
|
+
]
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
it('should successfully update PR with only version (no changes)', async () => {
|
|
840
|
+
const mockUpdatedPR = {
|
|
841
|
+
id: 1,
|
|
842
|
+
version: 1,
|
|
843
|
+
title: 'Original Title',
|
|
844
|
+
description: 'Original description',
|
|
845
|
+
state: 'OPEN'
|
|
846
|
+
};
|
|
847
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
848
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0);
|
|
849
|
+
expect(result.success).toBe(true);
|
|
850
|
+
expect(result.data).toBe(mockUpdatedPR);
|
|
851
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, {
|
|
852
|
+
version: 0
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
it('should successfully update PR with empty reviewers array', async () => {
|
|
856
|
+
const mockUpdatedPR = {
|
|
857
|
+
id: 1,
|
|
858
|
+
version: 1,
|
|
859
|
+
title: 'Test PR',
|
|
860
|
+
description: 'Test',
|
|
861
|
+
state: 'OPEN',
|
|
862
|
+
reviewers: []
|
|
863
|
+
};
|
|
864
|
+
PullRequestsService.update.mockResolvedValue(mockUpdatedPR);
|
|
865
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, undefined, undefined, []);
|
|
866
|
+
expect(result.success).toBe(true);
|
|
867
|
+
expect(PullRequestsService.update).toHaveBeenCalledWith(mockProjectKey, mockPullRequestId, mockRepositorySlug, expect.not.objectContaining({
|
|
868
|
+
reviewers: expect.anything()
|
|
869
|
+
}));
|
|
870
|
+
});
|
|
871
|
+
it('should handle API errors gracefully', async () => {
|
|
872
|
+
const mockError = new Error('Failed to update PR');
|
|
873
|
+
PullRequestsService.update.mockRejectedValue(mockError);
|
|
874
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 0, 'Updated Title');
|
|
875
|
+
expect(result.success).toBe(false);
|
|
876
|
+
expect(result.error).toBe('Failed to update PR');
|
|
877
|
+
});
|
|
878
|
+
it('should handle version conflict errors', async () => {
|
|
879
|
+
const mockError = new Error('Version conflict - PR has been modified');
|
|
880
|
+
PullRequestsService.update.mockRejectedValue(mockError);
|
|
881
|
+
const result = await bitbucketService.updatePullRequest(mockProjectKey, mockRepositorySlug, mockPullRequestId, 5, 'Updated Title');
|
|
882
|
+
expect(result.success).toBe(false);
|
|
883
|
+
expect(result.error).toBe('Version conflict - PR has been modified');
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
describe('getRequiredReviewers', () => {
|
|
887
|
+
it('should successfully get required reviewers', async () => {
|
|
888
|
+
const mockReviewersData = [
|
|
889
|
+
{
|
|
890
|
+
id: 1,
|
|
891
|
+
reviewers: [
|
|
892
|
+
{ name: 'reviewer1', emailAddress: 'reviewer1@example.com' },
|
|
893
|
+
{ name: 'reviewer2', emailAddress: 'reviewer2@example.com' }
|
|
894
|
+
]
|
|
895
|
+
}
|
|
896
|
+
];
|
|
897
|
+
PullRequestsService.getReviewers.mockResolvedValue(mockReviewersData);
|
|
898
|
+
const result = await bitbucketService.getRequiredReviewers(mockProjectKey, mockRepositorySlug, 'refs/heads/feature', 'refs/heads/main');
|
|
899
|
+
expect(result.success).toBe(true);
|
|
900
|
+
expect(result.data).toBe(mockReviewersData);
|
|
901
|
+
expect(PullRequestsService.getReviewers).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, undefined, // targetRepoId
|
|
902
|
+
undefined, // sourceRepoId
|
|
903
|
+
'refs/heads/feature', 'refs/heads/main');
|
|
904
|
+
});
|
|
905
|
+
it('should successfully get required reviewers with all parameters', async () => {
|
|
906
|
+
const mockReviewersData = [
|
|
907
|
+
{
|
|
908
|
+
id: 1,
|
|
909
|
+
reviewers: [
|
|
910
|
+
{ name: 'reviewer1', emailAddress: 'reviewer1@example.com' }
|
|
911
|
+
]
|
|
912
|
+
}
|
|
913
|
+
];
|
|
914
|
+
PullRequestsService.getReviewers.mockResolvedValue(mockReviewersData);
|
|
915
|
+
const result = await bitbucketService.getRequiredReviewers(mockProjectKey, mockRepositorySlug, 'refs/heads/feature', 'refs/heads/main', '123', // sourceRepoId
|
|
916
|
+
'456' // targetRepoId
|
|
917
|
+
);
|
|
918
|
+
expect(result.success).toBe(true);
|
|
919
|
+
expect(result.data).toBe(mockReviewersData);
|
|
920
|
+
expect(PullRequestsService.getReviewers).toHaveBeenCalledWith(mockProjectKey, mockRepositorySlug, '456', // targetRepoId
|
|
921
|
+
'123', // sourceRepoId
|
|
922
|
+
'refs/heads/feature', 'refs/heads/main');
|
|
923
|
+
});
|
|
924
|
+
it('should handle errors when getting required reviewers', async () => {
|
|
925
|
+
const mockError = new Error('API Error');
|
|
926
|
+
PullRequestsService.getReviewers.mockRejectedValue(mockError);
|
|
927
|
+
const result = await bitbucketService.getRequiredReviewers(mockProjectKey, mockRepositorySlug, 'refs/heads/feature', 'refs/heads/main');
|
|
928
|
+
expect(result.success).toBe(false);
|
|
929
|
+
expect(result.error).toBe('API Error');
|
|
930
|
+
});
|
|
931
|
+
});
|
|
232
932
|
describe('validateConfig', () => {
|
|
233
933
|
const originalEnv = process.env;
|
|
234
934
|
beforeEach(() => {
|