@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.
@@ -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(() => {