@atlassian-dc-mcp/bitbucket 0.9.9 → 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.
@@ -11,7 +11,12 @@ jest.mock('../bitbucket-client/index.js', () => ({
11
11
  PullRequestsService: {
12
12
  streamRawDiff2: jest.fn(),
13
13
  createComment2: jest.fn(),
14
- streamChanges1: jest.fn()
14
+ streamChanges1: jest.fn(),
15
+ create: jest.fn(),
16
+ update: jest.fn(),
17
+ getPage: jest.fn(),
18
+ getReviewers: jest.fn(),
19
+ get3: jest.fn()
15
20
  },
16
21
  OpenAPI: {
17
22
  BASE: '',
@@ -115,6 +120,576 @@ describe('BitbucketService', () => {
115
120
  });
116
121
  });
117
122
 
123
+ describe('getPullRequests', () => {
124
+ it('should successfully get pull requests with default parameters', async () => {
125
+ const mockPullRequestsData = {
126
+ values: [
127
+ {
128
+ id: 1,
129
+ title: 'Test PR 1',
130
+ state: 'OPEN',
131
+ author: { user: { name: 'user1' } }
132
+ },
133
+ {
134
+ id: 2,
135
+ title: 'Test PR 2',
136
+ state: 'OPEN',
137
+ author: { user: { name: 'user2' } }
138
+ }
139
+ ],
140
+ size: 2,
141
+ isLastPage: true,
142
+ start: 0,
143
+ limit: 25
144
+ };
145
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
146
+
147
+ const result = await bitbucketService.getPullRequests(
148
+ mockProjectKey,
149
+ mockRepositorySlug
150
+ );
151
+
152
+ expect(result.success).toBe(true);
153
+ expect(result.data).toBe(mockPullRequestsData);
154
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
155
+ mockProjectKey,
156
+ mockRepositorySlug,
157
+ undefined, // withAttributes
158
+ undefined, // at
159
+ undefined, // withProperties
160
+ undefined, // draft
161
+ undefined, // filterText
162
+ undefined, // state
163
+ undefined, // order
164
+ undefined, // direction
165
+ undefined, // start
166
+ 25 // limit
167
+ );
168
+ });
169
+
170
+ it('should successfully get pull requests with state filter', async () => {
171
+ const mockPullRequestsData = {
172
+ values: [
173
+ {
174
+ id: 1,
175
+ title: 'Merged PR',
176
+ state: 'MERGED',
177
+ author: { user: { name: 'user1' } }
178
+ }
179
+ ],
180
+ size: 1,
181
+ isLastPage: true,
182
+ start: 0,
183
+ limit: 25
184
+ };
185
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
186
+
187
+ const result = await bitbucketService.getPullRequests(
188
+ mockProjectKey,
189
+ mockRepositorySlug,
190
+ undefined, // withAttributes
191
+ undefined, // at
192
+ undefined, // withProperties
193
+ undefined, // draft
194
+ undefined, // filterText
195
+ 'MERGED' // state
196
+ );
197
+
198
+ expect(result.success).toBe(true);
199
+ expect(result.data).toBe(mockPullRequestsData);
200
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
201
+ mockProjectKey,
202
+ mockRepositorySlug,
203
+ undefined,
204
+ undefined,
205
+ undefined,
206
+ undefined,
207
+ undefined,
208
+ 'MERGED',
209
+ undefined,
210
+ undefined,
211
+ undefined,
212
+ 25
213
+ );
214
+ });
215
+
216
+ it('should successfully get pull requests with text filter', async () => {
217
+ const mockPullRequestsData = {
218
+ values: [
219
+ {
220
+ id: 1,
221
+ title: 'Fix bug in authentication',
222
+ state: 'OPEN',
223
+ author: { user: { name: 'user1' } }
224
+ }
225
+ ],
226
+ size: 1,
227
+ isLastPage: true,
228
+ start: 0,
229
+ limit: 25
230
+ };
231
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
232
+
233
+ const result = await bitbucketService.getPullRequests(
234
+ mockProjectKey,
235
+ mockRepositorySlug,
236
+ undefined, // withAttributes
237
+ undefined, // at
238
+ undefined, // withProperties
239
+ undefined, // draft
240
+ 'authentication' // filterText
241
+ );
242
+
243
+ expect(result.success).toBe(true);
244
+ expect(result.data).toBe(mockPullRequestsData);
245
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
246
+ mockProjectKey,
247
+ mockRepositorySlug,
248
+ undefined,
249
+ undefined,
250
+ undefined,
251
+ undefined,
252
+ 'authentication',
253
+ undefined,
254
+ undefined,
255
+ undefined,
256
+ undefined,
257
+ 25
258
+ );
259
+ });
260
+
261
+ it('should successfully get pull requests with branch filter', async () => {
262
+ const mockPullRequestsData = {
263
+ values: [
264
+ {
265
+ id: 1,
266
+ title: 'PR to master',
267
+ state: 'OPEN',
268
+ toRef: { id: 'refs/heads/master' }
269
+ }
270
+ ],
271
+ size: 1,
272
+ isLastPage: true,
273
+ start: 0,
274
+ limit: 25
275
+ };
276
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
277
+
278
+ const result = await bitbucketService.getPullRequests(
279
+ mockProjectKey,
280
+ mockRepositorySlug,
281
+ undefined, // withAttributes
282
+ 'refs/heads/master' // at
283
+ );
284
+
285
+ expect(result.success).toBe(true);
286
+ expect(result.data).toBe(mockPullRequestsData);
287
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
288
+ mockProjectKey,
289
+ mockRepositorySlug,
290
+ undefined,
291
+ 'refs/heads/master',
292
+ undefined,
293
+ undefined,
294
+ undefined,
295
+ undefined,
296
+ undefined,
297
+ undefined,
298
+ undefined,
299
+ 25
300
+ );
301
+ });
302
+
303
+ it('should successfully get pull requests with direction filter', async () => {
304
+ const mockPullRequestsData = {
305
+ values: [
306
+ {
307
+ id: 1,
308
+ title: 'Outgoing PR',
309
+ state: 'OPEN'
310
+ }
311
+ ],
312
+ size: 1,
313
+ isLastPage: true,
314
+ start: 0,
315
+ limit: 25
316
+ };
317
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
318
+
319
+ const result = await bitbucketService.getPullRequests(
320
+ mockProjectKey,
321
+ mockRepositorySlug,
322
+ undefined, // withAttributes
323
+ undefined, // at
324
+ undefined, // withProperties
325
+ undefined, // draft
326
+ undefined, // filterText
327
+ undefined, // state
328
+ undefined, // order
329
+ 'OUTGOING' // direction
330
+ );
331
+
332
+ expect(result.success).toBe(true);
333
+ expect(result.data).toBe(mockPullRequestsData);
334
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
335
+ mockProjectKey,
336
+ mockRepositorySlug,
337
+ undefined,
338
+ undefined,
339
+ undefined,
340
+ undefined,
341
+ undefined,
342
+ undefined,
343
+ undefined,
344
+ 'OUTGOING',
345
+ undefined,
346
+ 25
347
+ );
348
+ });
349
+
350
+ it('should successfully get pull requests with order filter', async () => {
351
+ const mockPullRequestsData = {
352
+ values: [
353
+ {
354
+ id: 1,
355
+ title: 'Oldest PR',
356
+ state: 'OPEN',
357
+ createdDate: 1234567890
358
+ },
359
+ {
360
+ id: 2,
361
+ title: 'Newer PR',
362
+ state: 'OPEN',
363
+ createdDate: 1234567900
364
+ }
365
+ ],
366
+ size: 2,
367
+ isLastPage: true,
368
+ start: 0,
369
+ limit: 25
370
+ };
371
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
372
+
373
+ const result = await bitbucketService.getPullRequests(
374
+ mockProjectKey,
375
+ mockRepositorySlug,
376
+ undefined, // withAttributes
377
+ undefined, // at
378
+ undefined, // withProperties
379
+ undefined, // draft
380
+ undefined, // filterText
381
+ undefined, // state
382
+ 'OLDEST' // order
383
+ );
384
+
385
+ expect(result.success).toBe(true);
386
+ expect(result.data).toBe(mockPullRequestsData);
387
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
388
+ mockProjectKey,
389
+ mockRepositorySlug,
390
+ undefined,
391
+ undefined,
392
+ undefined,
393
+ undefined,
394
+ undefined,
395
+ undefined,
396
+ 'OLDEST',
397
+ undefined,
398
+ undefined,
399
+ 25
400
+ );
401
+ });
402
+
403
+ it('should successfully get pull requests with draft filter', async () => {
404
+ const mockPullRequestsData = {
405
+ values: [
406
+ {
407
+ id: 1,
408
+ title: 'Draft PR',
409
+ state: 'OPEN',
410
+ draft: true
411
+ }
412
+ ],
413
+ size: 1,
414
+ isLastPage: true,
415
+ start: 0,
416
+ limit: 25
417
+ };
418
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
419
+
420
+ const result = await bitbucketService.getPullRequests(
421
+ mockProjectKey,
422
+ mockRepositorySlug,
423
+ undefined, // withAttributes
424
+ undefined, // at
425
+ undefined, // withProperties
426
+ 'true' // draft
427
+ );
428
+
429
+ expect(result.success).toBe(true);
430
+ expect(result.data).toBe(mockPullRequestsData);
431
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
432
+ mockProjectKey,
433
+ mockRepositorySlug,
434
+ undefined,
435
+ undefined,
436
+ undefined,
437
+ 'true',
438
+ undefined,
439
+ undefined,
440
+ undefined,
441
+ undefined,
442
+ undefined,
443
+ 25
444
+ );
445
+ });
446
+
447
+ it('should successfully get pull requests with pagination', async () => {
448
+ const mockPullRequestsData = {
449
+ values: [
450
+ {
451
+ id: 51,
452
+ title: 'PR 51',
453
+ state: 'OPEN'
454
+ }
455
+ ],
456
+ size: 1,
457
+ isLastPage: false,
458
+ start: 50,
459
+ limit: 10,
460
+ nextPageStart: 60
461
+ };
462
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
463
+
464
+ const result = await bitbucketService.getPullRequests(
465
+ mockProjectKey,
466
+ mockRepositorySlug,
467
+ undefined, // withAttributes
468
+ undefined, // at
469
+ undefined, // withProperties
470
+ undefined, // draft
471
+ undefined, // filterText
472
+ undefined, // state
473
+ undefined, // order
474
+ undefined, // direction
475
+ 50, // start
476
+ 10 // limit
477
+ );
478
+
479
+ expect(result.success).toBe(true);
480
+ expect(result.data).toBe(mockPullRequestsData);
481
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
482
+ mockProjectKey,
483
+ mockRepositorySlug,
484
+ undefined,
485
+ undefined,
486
+ undefined,
487
+ undefined,
488
+ undefined,
489
+ undefined,
490
+ undefined,
491
+ undefined,
492
+ 50,
493
+ 10
494
+ );
495
+ });
496
+
497
+ it('should successfully get pull requests with all parameters', async () => {
498
+ const mockPullRequestsData = {
499
+ values: [
500
+ {
501
+ id: 1,
502
+ title: 'Complete PR test',
503
+ state: 'OPEN',
504
+ draft: false
505
+ }
506
+ ],
507
+ size: 1,
508
+ isLastPage: true,
509
+ start: 0,
510
+ limit: 50
511
+ };
512
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
513
+
514
+ const result = await bitbucketService.getPullRequests(
515
+ mockProjectKey,
516
+ mockRepositorySlug,
517
+ 'true', // withAttributes
518
+ 'refs/heads/develop', // at
519
+ 'true', // withProperties
520
+ 'false', // draft
521
+ 'test', // filterText
522
+ 'ALL', // state
523
+ 'NEWEST', // order
524
+ 'INCOMING', // direction
525
+ 0, // start
526
+ 50 // limit
527
+ );
528
+
529
+ expect(result.success).toBe(true);
530
+ expect(result.data).toBe(mockPullRequestsData);
531
+ expect(PullRequestsService.getPage).toHaveBeenCalledWith(
532
+ mockProjectKey,
533
+ mockRepositorySlug,
534
+ 'true',
535
+ 'refs/heads/develop',
536
+ 'true',
537
+ 'false',
538
+ 'test',
539
+ 'ALL',
540
+ 'NEWEST',
541
+ 'INCOMING',
542
+ 0,
543
+ 50
544
+ );
545
+ });
546
+
547
+ it('should handle empty results', async () => {
548
+ const mockPullRequestsData = {
549
+ values: [],
550
+ size: 0,
551
+ isLastPage: true,
552
+ start: 0,
553
+ limit: 25
554
+ };
555
+ (PullRequestsService.getPage as jest.Mock).mockResolvedValue(mockPullRequestsData);
556
+
557
+ const result = await bitbucketService.getPullRequests(
558
+ mockProjectKey,
559
+ mockRepositorySlug
560
+ );
561
+
562
+ expect(result.success).toBe(true);
563
+ expect(result.data).toBe(mockPullRequestsData);
564
+ if (result.data) {
565
+ expect(result.data.values).toHaveLength(0);
566
+ }
567
+ });
568
+
569
+ it('should handle API errors gracefully', async () => {
570
+ const mockError = new Error('Failed to fetch pull requests');
571
+ (PullRequestsService.getPage as jest.Mock).mockRejectedValue(mockError);
572
+
573
+ const result = await bitbucketService.getPullRequests(
574
+ mockProjectKey,
575
+ mockRepositorySlug
576
+ );
577
+
578
+ expect(result.success).toBe(false);
579
+ expect(result.error).toBe('Failed to fetch pull requests');
580
+ });
581
+
582
+ it('should handle permission errors', async () => {
583
+ const mockError = new Error('Insufficient permissions');
584
+ (PullRequestsService.getPage as jest.Mock).mockRejectedValue(mockError);
585
+
586
+ const result = await bitbucketService.getPullRequests(
587
+ mockProjectKey,
588
+ mockRepositorySlug,
589
+ undefined,
590
+ undefined,
591
+ undefined,
592
+ undefined,
593
+ undefined,
594
+ 'OPEN'
595
+ );
596
+
597
+ expect(result.success).toBe(false);
598
+ expect(result.error).toBe('Insufficient permissions');
599
+ });
600
+ });
601
+
602
+ describe('getPullRequest', () => {
603
+ it('should successfully get a specific pull request by ID', async () => {
604
+ const mockPullRequestData = {
605
+ id: 123,
606
+ version: 1,
607
+ title: 'Feature: Add new functionality',
608
+ description: 'This PR adds new functionality',
609
+ state: 'OPEN',
610
+ open: true,
611
+ closed: false,
612
+ createdDate: 1234567890,
613
+ updatedDate: 1234567900,
614
+ fromRef: {
615
+ id: 'refs/heads/feature-branch',
616
+ displayId: 'feature-branch',
617
+ latestCommit: 'abc123',
618
+ repository: {
619
+ slug: 'test-repo',
620
+ project: { key: 'TEST' }
621
+ }
622
+ },
623
+ toRef: {
624
+ id: 'refs/heads/main',
625
+ displayId: 'main',
626
+ latestCommit: 'def456',
627
+ repository: {
628
+ slug: 'test-repo',
629
+ project: { key: 'TEST' }
630
+ }
631
+ },
632
+ author: {
633
+ user: {
634
+ name: 'testuser',
635
+ emailAddress: 'test@example.com',
636
+ displayName: 'Test User'
637
+ }
638
+ },
639
+ reviewers: [
640
+ {
641
+ user: { name: 'reviewer1', displayName: 'Reviewer One' },
642
+ approved: true
643
+ }
644
+ ],
645
+ participants: []
646
+ };
647
+ (PullRequestsService.get3 as jest.Mock).mockResolvedValue(mockPullRequestData);
648
+
649
+ const result = await bitbucketService.getPullRequest(
650
+ mockProjectKey,
651
+ mockRepositorySlug,
652
+ '123'
653
+ );
654
+
655
+ expect(result.success).toBe(true);
656
+ expect(result.data).toBe(mockPullRequestData);
657
+ expect(PullRequestsService.get3).toHaveBeenCalledWith(
658
+ mockProjectKey,
659
+ '123',
660
+ mockRepositorySlug
661
+ );
662
+ });
663
+
664
+ it('should handle errors when pull request does not exist', async () => {
665
+ const mockError = new Error('Not found');
666
+ (PullRequestsService.get3 as jest.Mock).mockRejectedValue(mockError);
667
+
668
+ const result = await bitbucketService.getPullRequest(
669
+ mockProjectKey,
670
+ mockRepositorySlug,
671
+ '999'
672
+ );
673
+
674
+ expect(result.success).toBe(false);
675
+ expect(result.error).toBe('Not found');
676
+ });
677
+
678
+ it('should handle permission errors', async () => {
679
+ const mockError = new Error('Insufficient permissions');
680
+ (PullRequestsService.get3 as jest.Mock).mockRejectedValue(mockError);
681
+
682
+ const result = await bitbucketService.getPullRequest(
683
+ mockProjectKey,
684
+ mockRepositorySlug,
685
+ '123'
686
+ );
687
+
688
+ expect(result.success).toBe(false);
689
+ expect(result.error).toBe('Insufficient permissions');
690
+ });
691
+ });
692
+
118
693
  describe('postPullRequestComment', () => {
119
694
  it('should successfully post a general PR comment', async () => {
120
695
  const mockComment = {
@@ -370,6 +945,566 @@ describe('BitbucketService', () => {
370
945
  });
371
946
  });
372
947
 
948
+ describe('createPullRequest', () => {
949
+ it('should successfully create a PR with minimal parameters', async () => {
950
+ const mockPullRequest = {
951
+ id: 1,
952
+ version: 0,
953
+ title: 'Test PR',
954
+ description: 'Test description',
955
+ state: 'OPEN',
956
+ fromRef: {
957
+ id: 'refs/heads/feature-branch',
958
+ repository: {
959
+ slug: mockRepositorySlug,
960
+ project: { key: mockProjectKey }
961
+ }
962
+ },
963
+ toRef: {
964
+ id: 'refs/heads/main',
965
+ repository: {
966
+ slug: mockRepositorySlug,
967
+ project: { key: mockProjectKey }
968
+ }
969
+ }
970
+ };
971
+ (PullRequestsService.create as jest.Mock).mockResolvedValue(mockPullRequest);
972
+
973
+ const result = await bitbucketService.createPullRequest(
974
+ mockProjectKey,
975
+ mockRepositorySlug,
976
+ 'Test PR',
977
+ 'Test description',
978
+ 'refs/heads/feature-branch',
979
+ 'refs/heads/main'
980
+ );
981
+
982
+ expect(result.success).toBe(true);
983
+ expect(result.data).toBe(mockPullRequest);
984
+ expect(PullRequestsService.create).toHaveBeenCalledWith(
985
+ mockProjectKey,
986
+ mockRepositorySlug,
987
+ {
988
+ title: 'Test PR',
989
+ description: 'Test description',
990
+ fromRef: {
991
+ id: 'refs/heads/feature-branch',
992
+ repository: {
993
+ slug: mockRepositorySlug,
994
+ project: { key: mockProjectKey }
995
+ }
996
+ },
997
+ toRef: {
998
+ id: 'refs/heads/main',
999
+ repository: {
1000
+ slug: mockRepositorySlug,
1001
+ project: { key: mockProjectKey }
1002
+ }
1003
+ }
1004
+ }
1005
+ );
1006
+ });
1007
+
1008
+ it('should successfully create a PR without description', async () => {
1009
+ const mockPullRequest = {
1010
+ id: 2,
1011
+ version: 0,
1012
+ title: 'Test PR without description',
1013
+ state: 'OPEN',
1014
+ fromRef: {
1015
+ id: 'refs/heads/feature-branch',
1016
+ repository: {
1017
+ slug: mockRepositorySlug,
1018
+ project: { key: mockProjectKey }
1019
+ }
1020
+ },
1021
+ toRef: {
1022
+ id: 'refs/heads/main',
1023
+ repository: {
1024
+ slug: mockRepositorySlug,
1025
+ project: { key: mockProjectKey }
1026
+ }
1027
+ }
1028
+ };
1029
+ (PullRequestsService.create as jest.Mock).mockResolvedValue(mockPullRequest);
1030
+
1031
+ const result = await bitbucketService.createPullRequest(
1032
+ mockProjectKey,
1033
+ mockRepositorySlug,
1034
+ 'Test PR without description',
1035
+ undefined,
1036
+ 'refs/heads/feature-branch',
1037
+ 'refs/heads/main'
1038
+ );
1039
+
1040
+ expect(result.success).toBe(true);
1041
+ expect(result.data).toBe(mockPullRequest);
1042
+ expect(PullRequestsService.create).toHaveBeenCalledWith(
1043
+ mockProjectKey,
1044
+ mockRepositorySlug,
1045
+ expect.objectContaining({
1046
+ title: 'Test PR without description',
1047
+ description: undefined
1048
+ })
1049
+ );
1050
+ });
1051
+
1052
+ it('should successfully create a PR with reviewers', async () => {
1053
+ const mockPullRequest = {
1054
+ id: 3,
1055
+ version: 0,
1056
+ title: 'Test PR with reviewers',
1057
+ description: 'PR with reviewers',
1058
+ state: 'OPEN',
1059
+ fromRef: {
1060
+ id: 'refs/heads/feature-branch',
1061
+ repository: {
1062
+ slug: mockRepositorySlug,
1063
+ project: { key: mockProjectKey }
1064
+ }
1065
+ },
1066
+ toRef: {
1067
+ id: 'refs/heads/main',
1068
+ repository: {
1069
+ slug: mockRepositorySlug,
1070
+ project: { key: mockProjectKey }
1071
+ }
1072
+ },
1073
+ reviewers: [
1074
+ { user: { name: 'reviewer1' } },
1075
+ { user: { name: 'reviewer2' } }
1076
+ ]
1077
+ };
1078
+ (PullRequestsService.create as jest.Mock).mockResolvedValue(mockPullRequest);
1079
+
1080
+ const result = await bitbucketService.createPullRequest(
1081
+ mockProjectKey,
1082
+ mockRepositorySlug,
1083
+ 'Test PR with reviewers',
1084
+ 'PR with reviewers',
1085
+ 'refs/heads/feature-branch',
1086
+ 'refs/heads/main',
1087
+ ['reviewer1', 'reviewer2']
1088
+ );
1089
+
1090
+ expect(result.success).toBe(true);
1091
+ expect(result.data).toBe(mockPullRequest);
1092
+ expect(PullRequestsService.create).toHaveBeenCalledWith(
1093
+ mockProjectKey,
1094
+ mockRepositorySlug,
1095
+ expect.objectContaining({
1096
+ title: 'Test PR with reviewers',
1097
+ reviewers: [
1098
+ { user: { name: 'reviewer1' } },
1099
+ { user: { name: 'reviewer2' } }
1100
+ ]
1101
+ })
1102
+ );
1103
+ });
1104
+
1105
+ it('should successfully create a PR with empty reviewers array', async () => {
1106
+ const mockPullRequest = {
1107
+ id: 4,
1108
+ version: 0,
1109
+ title: 'Test PR',
1110
+ description: 'Test',
1111
+ state: 'OPEN'
1112
+ };
1113
+ (PullRequestsService.create as jest.Mock).mockResolvedValue(mockPullRequest);
1114
+
1115
+ const result = await bitbucketService.createPullRequest(
1116
+ mockProjectKey,
1117
+ mockRepositorySlug,
1118
+ 'Test PR',
1119
+ 'Test',
1120
+ 'refs/heads/feature-branch',
1121
+ 'refs/heads/main',
1122
+ []
1123
+ );
1124
+
1125
+ expect(result.success).toBe(true);
1126
+ expect(PullRequestsService.create).toHaveBeenCalledWith(
1127
+ mockProjectKey,
1128
+ mockRepositorySlug,
1129
+ expect.not.objectContaining({
1130
+ reviewers: expect.anything()
1131
+ })
1132
+ );
1133
+ });
1134
+
1135
+ it('should handle API errors gracefully', async () => {
1136
+ const mockError = new Error('Failed to create PR');
1137
+ (PullRequestsService.create as jest.Mock).mockRejectedValue(mockError);
1138
+
1139
+ const result = await bitbucketService.createPullRequest(
1140
+ mockProjectKey,
1141
+ mockRepositorySlug,
1142
+ 'Test PR',
1143
+ 'Test description',
1144
+ 'refs/heads/feature-branch',
1145
+ 'refs/heads/main'
1146
+ );
1147
+
1148
+ expect(result.success).toBe(false);
1149
+ expect(result.error).toBe('Failed to create PR');
1150
+ });
1151
+ });
1152
+
1153
+ describe('updatePullRequest', () => {
1154
+ it('should successfully update PR with only title', async () => {
1155
+ const mockUpdatedPR = {
1156
+ id: 1,
1157
+ version: 1,
1158
+ title: 'Updated Title',
1159
+ description: 'Original description',
1160
+ state: 'OPEN'
1161
+ };
1162
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1163
+
1164
+ const result = await bitbucketService.updatePullRequest(
1165
+ mockProjectKey,
1166
+ mockRepositorySlug,
1167
+ mockPullRequestId,
1168
+ 0,
1169
+ 'Updated Title'
1170
+ );
1171
+
1172
+ expect(result.success).toBe(true);
1173
+ expect(result.data).toBe(mockUpdatedPR);
1174
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1175
+ mockProjectKey,
1176
+ mockPullRequestId,
1177
+ mockRepositorySlug,
1178
+ {
1179
+ version: 0,
1180
+ title: 'Updated Title'
1181
+ }
1182
+ );
1183
+ });
1184
+
1185
+ it('should successfully update PR with only description', async () => {
1186
+ const mockUpdatedPR = {
1187
+ id: 1,
1188
+ version: 1,
1189
+ title: 'Original Title',
1190
+ description: 'Updated description',
1191
+ state: 'OPEN'
1192
+ };
1193
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1194
+
1195
+ const result = await bitbucketService.updatePullRequest(
1196
+ mockProjectKey,
1197
+ mockRepositorySlug,
1198
+ mockPullRequestId,
1199
+ 0,
1200
+ undefined,
1201
+ 'Updated description'
1202
+ );
1203
+
1204
+ expect(result.success).toBe(true);
1205
+ expect(result.data).toBe(mockUpdatedPR);
1206
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1207
+ mockProjectKey,
1208
+ mockPullRequestId,
1209
+ mockRepositorySlug,
1210
+ {
1211
+ version: 0,
1212
+ description: 'Updated description'
1213
+ }
1214
+ );
1215
+ });
1216
+
1217
+ it('should successfully update PR with title and description', async () => {
1218
+ const mockUpdatedPR = {
1219
+ id: 1,
1220
+ version: 1,
1221
+ title: 'Updated Title',
1222
+ description: 'Updated description',
1223
+ state: 'OPEN'
1224
+ };
1225
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1226
+
1227
+ const result = await bitbucketService.updatePullRequest(
1228
+ mockProjectKey,
1229
+ mockRepositorySlug,
1230
+ mockPullRequestId,
1231
+ 0,
1232
+ 'Updated Title',
1233
+ 'Updated description'
1234
+ );
1235
+
1236
+ expect(result.success).toBe(true);
1237
+ expect(result.data).toBe(mockUpdatedPR);
1238
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1239
+ mockProjectKey,
1240
+ mockPullRequestId,
1241
+ mockRepositorySlug,
1242
+ {
1243
+ version: 0,
1244
+ title: 'Updated Title',
1245
+ description: 'Updated description'
1246
+ }
1247
+ );
1248
+ });
1249
+
1250
+ it('should successfully update PR with reviewers', async () => {
1251
+ const mockUpdatedPR = {
1252
+ id: 1,
1253
+ version: 1,
1254
+ title: 'Test PR',
1255
+ description: 'Test',
1256
+ state: 'OPEN',
1257
+ reviewers: [
1258
+ { user: { name: 'reviewer1' } },
1259
+ { user: { name: 'reviewer2' } },
1260
+ { user: { name: 'reviewer3' } }
1261
+ ]
1262
+ };
1263
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1264
+
1265
+ const result = await bitbucketService.updatePullRequest(
1266
+ mockProjectKey,
1267
+ mockRepositorySlug,
1268
+ mockPullRequestId,
1269
+ 0,
1270
+ undefined,
1271
+ undefined,
1272
+ ['reviewer1', 'reviewer2', 'reviewer3']
1273
+ );
1274
+
1275
+ expect(result.success).toBe(true);
1276
+ expect(result.data).toBe(mockUpdatedPR);
1277
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1278
+ mockProjectKey,
1279
+ mockPullRequestId,
1280
+ mockRepositorySlug,
1281
+ {
1282
+ version: 0,
1283
+ reviewers: [
1284
+ { user: { name: 'reviewer1' } },
1285
+ { user: { name: 'reviewer2' } },
1286
+ { user: { name: 'reviewer3' } }
1287
+ ]
1288
+ }
1289
+ );
1290
+ });
1291
+
1292
+ it('should successfully update PR with all parameters', async () => {
1293
+ const mockUpdatedPR = {
1294
+ id: 1,
1295
+ version: 2,
1296
+ title: 'Updated Title',
1297
+ description: 'Updated description',
1298
+ state: 'OPEN',
1299
+ reviewers: [
1300
+ { user: { name: 'newreviewer1' } },
1301
+ { user: { name: 'newreviewer2' } }
1302
+ ]
1303
+ };
1304
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1305
+
1306
+ const result = await bitbucketService.updatePullRequest(
1307
+ mockProjectKey,
1308
+ mockRepositorySlug,
1309
+ mockPullRequestId,
1310
+ 1,
1311
+ 'Updated Title',
1312
+ 'Updated description',
1313
+ ['newreviewer1', 'newreviewer2']
1314
+ );
1315
+
1316
+ expect(result.success).toBe(true);
1317
+ expect(result.data).toBe(mockUpdatedPR);
1318
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1319
+ mockProjectKey,
1320
+ mockPullRequestId,
1321
+ mockRepositorySlug,
1322
+ {
1323
+ version: 1,
1324
+ title: 'Updated Title',
1325
+ description: 'Updated description',
1326
+ reviewers: [
1327
+ { user: { name: 'newreviewer1' } },
1328
+ { user: { name: 'newreviewer2' } }
1329
+ ]
1330
+ }
1331
+ );
1332
+ });
1333
+
1334
+ it('should successfully update PR with only version (no changes)', async () => {
1335
+ const mockUpdatedPR = {
1336
+ id: 1,
1337
+ version: 1,
1338
+ title: 'Original Title',
1339
+ description: 'Original description',
1340
+ state: 'OPEN'
1341
+ };
1342
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1343
+
1344
+ const result = await bitbucketService.updatePullRequest(
1345
+ mockProjectKey,
1346
+ mockRepositorySlug,
1347
+ mockPullRequestId,
1348
+ 0
1349
+ );
1350
+
1351
+ expect(result.success).toBe(true);
1352
+ expect(result.data).toBe(mockUpdatedPR);
1353
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1354
+ mockProjectKey,
1355
+ mockPullRequestId,
1356
+ mockRepositorySlug,
1357
+ {
1358
+ version: 0
1359
+ }
1360
+ );
1361
+ });
1362
+
1363
+ it('should successfully update PR with empty reviewers array', async () => {
1364
+ const mockUpdatedPR = {
1365
+ id: 1,
1366
+ version: 1,
1367
+ title: 'Test PR',
1368
+ description: 'Test',
1369
+ state: 'OPEN',
1370
+ reviewers: []
1371
+ };
1372
+ (PullRequestsService.update as jest.Mock).mockResolvedValue(mockUpdatedPR);
1373
+
1374
+ const result = await bitbucketService.updatePullRequest(
1375
+ mockProjectKey,
1376
+ mockRepositorySlug,
1377
+ mockPullRequestId,
1378
+ 0,
1379
+ undefined,
1380
+ undefined,
1381
+ []
1382
+ );
1383
+
1384
+ expect(result.success).toBe(true);
1385
+ expect(PullRequestsService.update).toHaveBeenCalledWith(
1386
+ mockProjectKey,
1387
+ mockPullRequestId,
1388
+ mockRepositorySlug,
1389
+ expect.not.objectContaining({
1390
+ reviewers: expect.anything()
1391
+ })
1392
+ );
1393
+ });
1394
+
1395
+ it('should handle API errors gracefully', async () => {
1396
+ const mockError = new Error('Failed to update PR');
1397
+ (PullRequestsService.update as jest.Mock).mockRejectedValue(mockError);
1398
+
1399
+ const result = await bitbucketService.updatePullRequest(
1400
+ mockProjectKey,
1401
+ mockRepositorySlug,
1402
+ mockPullRequestId,
1403
+ 0,
1404
+ 'Updated Title'
1405
+ );
1406
+
1407
+ expect(result.success).toBe(false);
1408
+ expect(result.error).toBe('Failed to update PR');
1409
+ });
1410
+
1411
+ it('should handle version conflict errors', async () => {
1412
+ const mockError = new Error('Version conflict - PR has been modified');
1413
+ (PullRequestsService.update as jest.Mock).mockRejectedValue(mockError);
1414
+
1415
+ const result = await bitbucketService.updatePullRequest(
1416
+ mockProjectKey,
1417
+ mockRepositorySlug,
1418
+ mockPullRequestId,
1419
+ 5,
1420
+ 'Updated Title'
1421
+ );
1422
+
1423
+ expect(result.success).toBe(false);
1424
+ expect(result.error).toBe('Version conflict - PR has been modified');
1425
+ });
1426
+ });
1427
+
1428
+ describe('getRequiredReviewers', () => {
1429
+ it('should successfully get required reviewers', async () => {
1430
+ const mockReviewersData = [
1431
+ {
1432
+ id: 1,
1433
+ reviewers: [
1434
+ { name: 'reviewer1', emailAddress: 'reviewer1@example.com' },
1435
+ { name: 'reviewer2', emailAddress: 'reviewer2@example.com' }
1436
+ ]
1437
+ }
1438
+ ];
1439
+ (PullRequestsService.getReviewers as jest.Mock).mockResolvedValue(mockReviewersData);
1440
+
1441
+ const result = await bitbucketService.getRequiredReviewers(
1442
+ mockProjectKey,
1443
+ mockRepositorySlug,
1444
+ 'refs/heads/feature',
1445
+ 'refs/heads/main'
1446
+ );
1447
+
1448
+ expect(result.success).toBe(true);
1449
+ expect(result.data).toBe(mockReviewersData);
1450
+ expect(PullRequestsService.getReviewers).toHaveBeenCalledWith(
1451
+ mockProjectKey,
1452
+ mockRepositorySlug,
1453
+ undefined, // targetRepoId
1454
+ undefined, // sourceRepoId
1455
+ 'refs/heads/feature',
1456
+ 'refs/heads/main'
1457
+ );
1458
+ });
1459
+
1460
+ it('should successfully get required reviewers with all parameters', async () => {
1461
+ const mockReviewersData = [
1462
+ {
1463
+ id: 1,
1464
+ reviewers: [
1465
+ { name: 'reviewer1', emailAddress: 'reviewer1@example.com' }
1466
+ ]
1467
+ }
1468
+ ];
1469
+ (PullRequestsService.getReviewers as jest.Mock).mockResolvedValue(mockReviewersData);
1470
+
1471
+ const result = await bitbucketService.getRequiredReviewers(
1472
+ mockProjectKey,
1473
+ mockRepositorySlug,
1474
+ 'refs/heads/feature',
1475
+ 'refs/heads/main',
1476
+ '123', // sourceRepoId
1477
+ '456' // targetRepoId
1478
+ );
1479
+
1480
+ expect(result.success).toBe(true);
1481
+ expect(result.data).toBe(mockReviewersData);
1482
+ expect(PullRequestsService.getReviewers).toHaveBeenCalledWith(
1483
+ mockProjectKey,
1484
+ mockRepositorySlug,
1485
+ '456', // targetRepoId
1486
+ '123', // sourceRepoId
1487
+ 'refs/heads/feature',
1488
+ 'refs/heads/main'
1489
+ );
1490
+ });
1491
+
1492
+ it('should handle errors when getting required reviewers', async () => {
1493
+ const mockError = new Error('API Error');
1494
+ (PullRequestsService.getReviewers as jest.Mock).mockRejectedValue(mockError);
1495
+
1496
+ const result = await bitbucketService.getRequiredReviewers(
1497
+ mockProjectKey,
1498
+ mockRepositorySlug,
1499
+ 'refs/heads/feature',
1500
+ 'refs/heads/main'
1501
+ );
1502
+
1503
+ expect(result.success).toBe(false);
1504
+ expect(result.error).toBe('API Error');
1505
+ });
1506
+ });
1507
+
373
1508
  describe('validateConfig', () => {
374
1509
  const originalEnv = process.env;
375
1510