github-issue-tower-defence-management 1.104.0 → 1.104.2
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 +14 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +83 -58
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +7 -2
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js +21 -2
- package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +36 -6
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/StartPreparationUseCase.js +2 -1
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/resolveAllowedIssueAuthors.js +8 -0
- package/bin/domain/usecases/resolveAllowedIssueAuthors.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +36 -0
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +23 -1
- package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +109 -0
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +8 -2
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +182 -0
- package/src/domain/usecases/IssueRejectionEvaluator.ts +35 -1
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +303 -75
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +42 -7
- package/src/domain/usecases/StartPreparationUseCase.test.ts +202 -88
- package/src/domain/usecases/StartPreparationUseCase.ts +2 -1
- package/src/domain/usecases/resolveAllowedIssueAuthors.test.ts +51 -0
- package/src/domain/usecases/resolveAllowedIssueAuthors.ts +6 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -0
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts +5 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
- package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts +1 -0
- package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/resolveAllowedIssueAuthors.d.ts +5 -0
- package/types/domain/usecases/resolveAllowedIssueAuthors.d.ts.map +1 -0
|
@@ -79,7 +79,7 @@ const createMockIssue = (overrides: Partial<Issue> = {}): Issue => ({
|
|
|
79
79
|
isInProgress: false,
|
|
80
80
|
isClosed: false,
|
|
81
81
|
createdAt: new Date(),
|
|
82
|
-
author: '',
|
|
82
|
+
author: 'owner',
|
|
83
83
|
closingIssueReferenceUrls: [],
|
|
84
84
|
...overrides,
|
|
85
85
|
});
|
|
@@ -93,7 +93,20 @@ const createMockPullRequest = (overrides: Partial<Issue> = {}): Issue =>
|
|
|
93
93
|
...overrides,
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
type RelatedPrLike = {
|
|
97
|
+
url: string;
|
|
98
|
+
isConflicted: boolean;
|
|
99
|
+
isPassedAllCiJob: boolean;
|
|
100
|
+
isCiStateSuccess: boolean;
|
|
101
|
+
isResolvedAllReviewComments: boolean;
|
|
102
|
+
isBranchOutOfDate: boolean;
|
|
103
|
+
missingRequiredCheckNames: string[];
|
|
104
|
+
isDraft?: boolean;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const createReadyPr = (
|
|
108
|
+
url = 'https://github.com/user/repo/pull/1',
|
|
109
|
+
): RelatedPrLike => ({
|
|
97
110
|
url,
|
|
98
111
|
isConflicted: false,
|
|
99
112
|
isPassedAllCiJob: true,
|
|
@@ -103,6 +116,37 @@ const createReadyPr = (url = 'https://github.com/user/repo/pull/1') => ({
|
|
|
103
116
|
missingRequiredCheckNames: [],
|
|
104
117
|
});
|
|
105
118
|
|
|
119
|
+
// Builds the in-memory linkage the production code now relies on instead of the
|
|
120
|
+
// per-issue findRelatedOpenPRs timeline query: each related pull request becomes
|
|
121
|
+
// an open PR project item whose closingIssueReferenceUrls points at the AQC
|
|
122
|
+
// issue, and getOpenPullRequest is routed by URL to return that PR's status.
|
|
123
|
+
// This proves the review-readiness outcome is identical to the previous
|
|
124
|
+
// findRelatedOpenPRs-based path while removing the per-issue timeline call.
|
|
125
|
+
const linkRelatedOpenPrsToIssue = (
|
|
126
|
+
mockIssueRepository: {
|
|
127
|
+
getAllIssues: jest.Mock;
|
|
128
|
+
getOpenPullRequest: jest.Mock;
|
|
129
|
+
},
|
|
130
|
+
issue: Issue,
|
|
131
|
+
relatedPrs: RelatedPrLike[],
|
|
132
|
+
): void => {
|
|
133
|
+
const prItems = relatedPrs.map((pr, index) =>
|
|
134
|
+
createMockPullRequest({
|
|
135
|
+
url: pr.url,
|
|
136
|
+
number: 1000 + index,
|
|
137
|
+
closingIssueReferenceUrls: [issue.url],
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
141
|
+
issues: [issue, ...prItems],
|
|
142
|
+
cacheUsed: false,
|
|
143
|
+
});
|
|
144
|
+
const prByUrl = new Map(relatedPrs.map((pr) => [pr.url, pr]));
|
|
145
|
+
mockIssueRepository.getOpenPullRequest.mockImplementation((prUrl: string) =>
|
|
146
|
+
Promise.resolve(prByUrl.get(prUrl) ?? null),
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
106
150
|
describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
107
151
|
let mockProjectRepository: {
|
|
108
152
|
findProjectIdByUrl: jest.Mock;
|
|
@@ -171,6 +215,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
171
215
|
await useCase.run({
|
|
172
216
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
173
217
|
allowIssueCacheMinutes: 10,
|
|
218
|
+
allowedIssueAuthors: ['owner'],
|
|
174
219
|
});
|
|
175
220
|
|
|
176
221
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -190,6 +235,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
190
235
|
await useCase.run({
|
|
191
236
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
192
237
|
allowIssueCacheMinutes: 10,
|
|
238
|
+
allowedIssueAuthors: ['owner'],
|
|
193
239
|
});
|
|
194
240
|
|
|
195
241
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -204,11 +250,11 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
204
250
|
issues: [issue],
|
|
205
251
|
cacheUsed: false,
|
|
206
252
|
});
|
|
207
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
208
253
|
|
|
209
254
|
await useCase.run({
|
|
210
255
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
211
256
|
allowIssueCacheMinutes: 10,
|
|
257
|
+
allowedIssueAuthors: ['owner'],
|
|
212
258
|
});
|
|
213
259
|
|
|
214
260
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -235,12 +281,12 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
235
281
|
issues: [issue],
|
|
236
282
|
cacheUsed: false,
|
|
237
283
|
});
|
|
238
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
239
284
|
|
|
240
285
|
await useCase.run({
|
|
241
286
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
242
287
|
allowIssueCacheMinutes: 10,
|
|
243
288
|
labelsAsLlmAgentName: ['story', 'chore', 'accounting'],
|
|
289
|
+
allowedIssueAuthors: ['owner'],
|
|
244
290
|
});
|
|
245
291
|
|
|
246
292
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -256,12 +302,12 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
256
302
|
issues: [issue],
|
|
257
303
|
cacheUsed: false,
|
|
258
304
|
});
|
|
259
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
260
305
|
|
|
261
306
|
await useCase.run({
|
|
262
307
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
263
308
|
allowIssueCacheMinutes: 10,
|
|
264
309
|
labelsAsLlmAgentName: ['story', 'chore'],
|
|
310
|
+
allowedIssueAuthors: ['owner'],
|
|
265
311
|
});
|
|
266
312
|
|
|
267
313
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -277,11 +323,11 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
277
323
|
issues: [issue],
|
|
278
324
|
cacheUsed: false,
|
|
279
325
|
});
|
|
280
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
281
326
|
|
|
282
327
|
await useCase.run({
|
|
283
328
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
284
329
|
allowIssueCacheMinutes: 10,
|
|
330
|
+
allowedIssueAuthors: ['owner'],
|
|
285
331
|
});
|
|
286
332
|
|
|
287
333
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -304,12 +350,12 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
304
350
|
issues: [issue],
|
|
305
351
|
cacheUsed: false,
|
|
306
352
|
});
|
|
307
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
308
353
|
|
|
309
354
|
await useCase.run({
|
|
310
355
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
311
356
|
allowIssueCacheMinutes: 10,
|
|
312
357
|
labelsAsLlmAgentName: null,
|
|
358
|
+
allowedIssueAuthors: ['owner'],
|
|
313
359
|
});
|
|
314
360
|
|
|
315
361
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -323,17 +369,12 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
323
369
|
const issue = createMockIssue({
|
|
324
370
|
status: 'Awaiting Quality Check',
|
|
325
371
|
});
|
|
326
|
-
mockIssueRepository
|
|
327
|
-
issues: [issue],
|
|
328
|
-
cacheUsed: false,
|
|
329
|
-
});
|
|
330
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
331
|
-
createReadyPr(),
|
|
332
|
-
]);
|
|
372
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [createReadyPr()]);
|
|
333
373
|
|
|
334
374
|
await useCase.run({
|
|
335
375
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
336
376
|
allowIssueCacheMinutes: 10,
|
|
377
|
+
allowedIssueAuthors: ['owner'],
|
|
337
378
|
});
|
|
338
379
|
|
|
339
380
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -344,17 +385,14 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
344
385
|
const issue = createMockIssue({
|
|
345
386
|
status: 'Awaiting Quality Check',
|
|
346
387
|
});
|
|
347
|
-
mockIssueRepository
|
|
348
|
-
issues: [issue],
|
|
349
|
-
cacheUsed: false,
|
|
350
|
-
});
|
|
351
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
388
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
352
389
|
{ ...createReadyPr(), isConflicted: true },
|
|
353
390
|
]);
|
|
354
391
|
|
|
355
392
|
await useCase.run({
|
|
356
393
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
357
394
|
allowIssueCacheMinutes: 10,
|
|
395
|
+
allowedIssueAuthors: ['owner'],
|
|
358
396
|
});
|
|
359
397
|
|
|
360
398
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -376,11 +414,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
376
414
|
const issue = createMockIssue({
|
|
377
415
|
status: 'Awaiting Quality Check',
|
|
378
416
|
});
|
|
379
|
-
mockIssueRepository
|
|
380
|
-
issues: [issue],
|
|
381
|
-
cacheUsed: false,
|
|
382
|
-
});
|
|
383
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
417
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
384
418
|
{
|
|
385
419
|
...createReadyPr(),
|
|
386
420
|
isPassedAllCiJob: false,
|
|
@@ -391,6 +425,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
391
425
|
await useCase.run({
|
|
392
426
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
393
427
|
allowIssueCacheMinutes: 10,
|
|
428
|
+
allowedIssueAuthors: ['owner'],
|
|
394
429
|
});
|
|
395
430
|
|
|
396
431
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -412,17 +447,14 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
412
447
|
const issue = createMockIssue({
|
|
413
448
|
status: 'Awaiting Quality Check',
|
|
414
449
|
});
|
|
415
|
-
mockIssueRepository
|
|
416
|
-
issues: [issue],
|
|
417
|
-
cacheUsed: false,
|
|
418
|
-
});
|
|
419
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
450
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
420
451
|
{ ...createReadyPr(), isResolvedAllReviewComments: false },
|
|
421
452
|
]);
|
|
422
453
|
|
|
423
454
|
await useCase.run({
|
|
424
455
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
425
456
|
allowIssueCacheMinutes: 10,
|
|
457
|
+
allowedIssueAuthors: ['owner'],
|
|
426
458
|
});
|
|
427
459
|
|
|
428
460
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -444,17 +476,14 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
444
476
|
const issue = createMockIssue({
|
|
445
477
|
status: 'Awaiting Quality Check',
|
|
446
478
|
});
|
|
447
|
-
mockIssueRepository
|
|
448
|
-
issues: [issue],
|
|
449
|
-
cacheUsed: false,
|
|
450
|
-
});
|
|
451
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
479
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
452
480
|
{ ...createReadyPr(), isDraft: true },
|
|
453
481
|
]);
|
|
454
482
|
|
|
455
483
|
await useCase.run({
|
|
456
484
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
457
485
|
allowIssueCacheMinutes: 10,
|
|
486
|
+
allowedIssueAuthors: ['owner'],
|
|
458
487
|
});
|
|
459
488
|
|
|
460
489
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -476,11 +505,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
476
505
|
const issue = createMockIssue({
|
|
477
506
|
status: 'Awaiting Quality Check',
|
|
478
507
|
});
|
|
479
|
-
mockIssueRepository
|
|
480
|
-
issues: [issue],
|
|
481
|
-
cacheUsed: false,
|
|
482
|
-
});
|
|
483
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
508
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
484
509
|
createReadyPr('https://github.com/user/repo/pull/1'),
|
|
485
510
|
createReadyPr('https://github.com/user/repo/pull/2'),
|
|
486
511
|
]);
|
|
@@ -488,6 +513,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
488
513
|
await useCase.run({
|
|
489
514
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
490
515
|
allowIssueCacheMinutes: 10,
|
|
516
|
+
allowedIssueAuthors: ['owner'],
|
|
491
517
|
});
|
|
492
518
|
|
|
493
519
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -509,11 +535,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
509
535
|
const issue = createMockIssue({
|
|
510
536
|
status: 'Awaiting Quality Check',
|
|
511
537
|
});
|
|
512
|
-
mockIssueRepository
|
|
513
|
-
issues: [issue],
|
|
514
|
-
cacheUsed: false,
|
|
515
|
-
});
|
|
516
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
538
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
517
539
|
{
|
|
518
540
|
...createReadyPr(),
|
|
519
541
|
isPassedAllCiJob: false,
|
|
@@ -525,6 +547,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
525
547
|
await useCase.run({
|
|
526
548
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
527
549
|
allowIssueCacheMinutes: 10,
|
|
550
|
+
allowedIssueAuthors: ['owner'],
|
|
528
551
|
});
|
|
529
552
|
|
|
530
553
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -542,17 +565,151 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
542
565
|
);
|
|
543
566
|
});
|
|
544
567
|
|
|
545
|
-
describe('
|
|
546
|
-
|
|
568
|
+
describe('in-memory related-PR derivation (no per-issue timeline call)', () => {
|
|
569
|
+
it('should never call findRelatedOpenPRs while sweeping Awaiting Quality Check issues', async () => {
|
|
570
|
+
const readyIssue = createMockIssue({
|
|
571
|
+
status: 'Awaiting Quality Check',
|
|
572
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
573
|
+
});
|
|
574
|
+
const notReadyIssue = createMockIssue({
|
|
575
|
+
status: 'Awaiting Quality Check',
|
|
576
|
+
number: 2,
|
|
577
|
+
url: 'https://github.com/user/repo/issues/2',
|
|
578
|
+
});
|
|
579
|
+
const readyPrItem = createMockPullRequest({
|
|
580
|
+
status: 'In Progress',
|
|
581
|
+
url: 'https://github.com/user/repo/pull/100',
|
|
582
|
+
number: 100,
|
|
583
|
+
closingIssueReferenceUrls: ['https://github.com/user/repo/issues/1'],
|
|
584
|
+
});
|
|
585
|
+
const conflictedPrItem = createMockPullRequest({
|
|
586
|
+
status: 'In Progress',
|
|
587
|
+
url: 'https://github.com/user/repo/pull/200',
|
|
588
|
+
number: 200,
|
|
589
|
+
closingIssueReferenceUrls: ['https://github.com/user/repo/issues/2'],
|
|
590
|
+
});
|
|
591
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
592
|
+
issues: [readyIssue, notReadyIssue, readyPrItem, conflictedPrItem],
|
|
593
|
+
cacheUsed: false,
|
|
594
|
+
});
|
|
595
|
+
mockIssueRepository.getOpenPullRequest.mockImplementation(
|
|
596
|
+
(prUrl: string) => {
|
|
597
|
+
if (prUrl === 'https://github.com/user/repo/pull/100') {
|
|
598
|
+
return Promise.resolve(createReadyPr(prUrl));
|
|
599
|
+
}
|
|
600
|
+
if (prUrl === 'https://github.com/user/repo/pull/200') {
|
|
601
|
+
return Promise.resolve({
|
|
602
|
+
...createReadyPr(prUrl),
|
|
603
|
+
isConflicted: true,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return Promise.resolve(null);
|
|
607
|
+
},
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
await useCase.run({
|
|
611
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
612
|
+
allowIssueCacheMinutes: 10,
|
|
613
|
+
allowedIssueAuthors: ['owner'],
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
expect(mockIssueRepository.findRelatedOpenPRs).not.toHaveBeenCalled();
|
|
617
|
+
expect(mockIssueRepository.getOpenPullRequest).toHaveBeenCalledWith(
|
|
618
|
+
'https://github.com/user/repo/pull/100',
|
|
619
|
+
);
|
|
620
|
+
expect(mockIssueRepository.getOpenPullRequest).toHaveBeenCalledWith(
|
|
621
|
+
'https://github.com/user/repo/pull/200',
|
|
622
|
+
);
|
|
623
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledTimes(1);
|
|
624
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
625
|
+
mockProject,
|
|
626
|
+
notReadyIssue,
|
|
627
|
+
'awaiting-workspace-id',
|
|
628
|
+
);
|
|
629
|
+
expect(mockIssueCommentRepository.createComment).toHaveBeenCalledWith(
|
|
630
|
+
notReadyIssue,
|
|
631
|
+
expect.stringContaining('PULL_REQUEST_CONFLICTED'),
|
|
632
|
+
);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should match a PR to its issue via closingIssueReferenceUrls even when the PR item is listed before the issue', async () => {
|
|
547
636
|
const issue = createMockIssue({
|
|
548
637
|
status: 'Awaiting Quality Check',
|
|
549
|
-
|
|
638
|
+
url: 'https://github.com/user/repo/issues/42',
|
|
639
|
+
number: 42,
|
|
640
|
+
});
|
|
641
|
+
const prItem = createMockPullRequest({
|
|
642
|
+
status: 'In Progress',
|
|
643
|
+
url: 'https://github.com/user/repo/pull/77',
|
|
644
|
+
number: 77,
|
|
645
|
+
closingIssueReferenceUrls: ['https://github.com/user/repo/issues/42'],
|
|
550
646
|
});
|
|
551
647
|
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
552
|
-
issues: [issue],
|
|
648
|
+
issues: [prItem, issue],
|
|
553
649
|
cacheUsed: false,
|
|
554
650
|
});
|
|
555
|
-
mockIssueRepository.
|
|
651
|
+
mockIssueRepository.getOpenPullRequest.mockResolvedValue(
|
|
652
|
+
createReadyPr('https://github.com/user/repo/pull/77'),
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
await useCase.run({
|
|
656
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
657
|
+
allowIssueCacheMinutes: 10,
|
|
658
|
+
allowedIssueAuthors: ['owner'],
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
expect(mockIssueRepository.getOpenPullRequest).toHaveBeenCalledWith(
|
|
662
|
+
'https://github.com/user/repo/pull/77',
|
|
663
|
+
);
|
|
664
|
+
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
665
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should not match a closed PR even when its closingIssueReferenceUrls points at the issue', async () => {
|
|
669
|
+
const issue = createMockIssue({
|
|
670
|
+
status: 'Awaiting Quality Check',
|
|
671
|
+
url: 'https://github.com/user/repo/issues/9',
|
|
672
|
+
number: 9,
|
|
673
|
+
});
|
|
674
|
+
const closedPrItem = createMockPullRequest({
|
|
675
|
+
status: 'In Progress',
|
|
676
|
+
url: 'https://github.com/user/repo/pull/9',
|
|
677
|
+
number: 9,
|
|
678
|
+
isClosed: true,
|
|
679
|
+
state: 'CLOSED',
|
|
680
|
+
closingIssueReferenceUrls: ['https://github.com/user/repo/issues/9'],
|
|
681
|
+
});
|
|
682
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
683
|
+
issues: [issue, closedPrItem],
|
|
684
|
+
cacheUsed: false,
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
await useCase.run({
|
|
688
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
689
|
+
allowIssueCacheMinutes: 10,
|
|
690
|
+
allowedIssueAuthors: ['owner'],
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
expect(mockIssueRepository.getOpenPullRequest).not.toHaveBeenCalled();
|
|
694
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
695
|
+
mockProject,
|
|
696
|
+
issue,
|
|
697
|
+
'awaiting-workspace-id',
|
|
698
|
+
);
|
|
699
|
+
expect(mockIssueCommentRepository.createComment).toHaveBeenCalledWith(
|
|
700
|
+
issue,
|
|
701
|
+
expect.stringContaining('PULL_REQUEST_NOT_FOUND'),
|
|
702
|
+
);
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
describe('change-target label auto-approve', () => {
|
|
707
|
+
const setupReadyIssue = (labels: string[]) => {
|
|
708
|
+
const issue = createMockIssue({
|
|
709
|
+
status: 'Awaiting Quality Check',
|
|
710
|
+
labels,
|
|
711
|
+
});
|
|
712
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
556
713
|
createReadyPr(),
|
|
557
714
|
]);
|
|
558
715
|
return issue;
|
|
@@ -562,6 +719,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
562
719
|
useCase.run({
|
|
563
720
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
564
721
|
allowIssueCacheMinutes: 10,
|
|
722
|
+
allowedIssueAuthors: ['owner'],
|
|
565
723
|
});
|
|
566
724
|
|
|
567
725
|
it('should not approve PR when issue has no change-target label', async () => {
|
|
@@ -684,11 +842,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
684
842
|
status: 'Awaiting Quality Check',
|
|
685
843
|
labels: ['change-target:src/domain'],
|
|
686
844
|
});
|
|
687
|
-
mockIssueRepository
|
|
688
|
-
issues: [issue],
|
|
689
|
-
cacheUsed: false,
|
|
690
|
-
});
|
|
691
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
845
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, []);
|
|
692
846
|
|
|
693
847
|
await runCycle();
|
|
694
848
|
|
|
@@ -708,11 +862,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
708
862
|
status: 'Awaiting Quality Check',
|
|
709
863
|
labels: ['change-target:src/domain'],
|
|
710
864
|
});
|
|
711
|
-
mockIssueRepository
|
|
712
|
-
issues: [issue],
|
|
713
|
-
cacheUsed: false,
|
|
714
|
-
});
|
|
715
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
865
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
716
866
|
{ ...createReadyPr(), isConflicted: true },
|
|
717
867
|
]);
|
|
718
868
|
|
|
@@ -734,11 +884,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
734
884
|
status: 'Awaiting Quality Check',
|
|
735
885
|
labels: ['llm-agent', 'change-target:src/domain'],
|
|
736
886
|
});
|
|
737
|
-
mockIssueRepository
|
|
738
|
-
issues: [issue],
|
|
739
|
-
cacheUsed: false,
|
|
740
|
-
});
|
|
741
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
|
|
887
|
+
linkRelatedOpenPrsToIssue(mockIssueRepository, issue, [
|
|
742
888
|
createReadyPr(),
|
|
743
889
|
]);
|
|
744
890
|
|
|
@@ -773,6 +919,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
773
919
|
await useCase.run({
|
|
774
920
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
775
921
|
allowIssueCacheMinutes: 10,
|
|
922
|
+
allowedIssueAuthors: ['owner'],
|
|
776
923
|
changeTargetPathAliases: {
|
|
777
924
|
adapters: 'src/domain/usecases/adapter-interfaces',
|
|
778
925
|
},
|
|
@@ -792,6 +939,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
792
939
|
await useCase.run({
|
|
793
940
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
794
941
|
allowIssueCacheMinutes: 10,
|
|
942
|
+
allowedIssueAuthors: ['owner'],
|
|
795
943
|
changeTargetPathAliases: {
|
|
796
944
|
adapters: 'src/domain/usecases/adapter-interfaces',
|
|
797
945
|
},
|
|
@@ -815,6 +963,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
815
963
|
await useCase.run({
|
|
816
964
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
817
965
|
allowIssueCacheMinutes: 10,
|
|
966
|
+
allowedIssueAuthors: ['owner'],
|
|
818
967
|
});
|
|
819
968
|
|
|
820
969
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -836,6 +985,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
836
985
|
await useCase.run({
|
|
837
986
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
838
987
|
allowIssueCacheMinutes: 10,
|
|
988
|
+
allowedIssueAuthors: ['owner'],
|
|
839
989
|
});
|
|
840
990
|
|
|
841
991
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -856,6 +1006,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
856
1006
|
await useCase.run({
|
|
857
1007
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
858
1008
|
allowIssueCacheMinutes: 10,
|
|
1009
|
+
allowedIssueAuthors: ['owner'],
|
|
859
1010
|
});
|
|
860
1011
|
|
|
861
1012
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -876,6 +1027,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
876
1027
|
await useCase.run({
|
|
877
1028
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
878
1029
|
allowIssueCacheMinutes: 10,
|
|
1030
|
+
allowedIssueAuthors: ['owner'],
|
|
879
1031
|
});
|
|
880
1032
|
|
|
881
1033
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -899,6 +1051,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
899
1051
|
await useCase.run({
|
|
900
1052
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
901
1053
|
allowIssueCacheMinutes: 10,
|
|
1054
|
+
allowedIssueAuthors: ['owner'],
|
|
902
1055
|
});
|
|
903
1056
|
|
|
904
1057
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -938,6 +1091,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
938
1091
|
await useCase.run({
|
|
939
1092
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
940
1093
|
allowIssueCacheMinutes: 10,
|
|
1094
|
+
allowedIssueAuthors: ['owner'],
|
|
941
1095
|
});
|
|
942
1096
|
|
|
943
1097
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -972,6 +1126,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
972
1126
|
await useCase.run({
|
|
973
1127
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
974
1128
|
allowIssueCacheMinutes: 10,
|
|
1129
|
+
allowedIssueAuthors: ['owner'],
|
|
975
1130
|
});
|
|
976
1131
|
|
|
977
1132
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -1008,6 +1163,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1008
1163
|
await useCase.run({
|
|
1009
1164
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1010
1165
|
allowIssueCacheMinutes: 10,
|
|
1166
|
+
allowedIssueAuthors: ['owner'],
|
|
1011
1167
|
});
|
|
1012
1168
|
|
|
1013
1169
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -1042,6 +1198,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1042
1198
|
await useCase.run({
|
|
1043
1199
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1044
1200
|
allowIssueCacheMinutes: 10,
|
|
1201
|
+
allowedIssueAuthors: ['owner'],
|
|
1045
1202
|
});
|
|
1046
1203
|
|
|
1047
1204
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -1073,6 +1230,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1073
1230
|
await useCase.run({
|
|
1074
1231
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1075
1232
|
allowIssueCacheMinutes: 10,
|
|
1233
|
+
allowedIssueAuthors: ['owner'],
|
|
1076
1234
|
});
|
|
1077
1235
|
|
|
1078
1236
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -1110,6 +1268,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1110
1268
|
await useCase.run({
|
|
1111
1269
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1112
1270
|
allowIssueCacheMinutes: 10,
|
|
1271
|
+
allowedIssueAuthors: ['owner'],
|
|
1113
1272
|
});
|
|
1114
1273
|
|
|
1115
1274
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
@@ -1154,6 +1313,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1154
1313
|
await useCase.run({
|
|
1155
1314
|
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1156
1315
|
allowIssueCacheMinutes: 10,
|
|
1316
|
+
allowedIssueAuthors: ['owner'],
|
|
1157
1317
|
});
|
|
1158
1318
|
|
|
1159
1319
|
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
@@ -1223,7 +1383,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1223
1383
|
);
|
|
1224
1384
|
});
|
|
1225
1385
|
|
|
1226
|
-
it('should
|
|
1386
|
+
it('should skip an Unread pull request authored by a dependency-update bot not in allowedIssueAuthors', async () => {
|
|
1227
1387
|
const pullRequest = createMockPullRequest({
|
|
1228
1388
|
status: 'Unread',
|
|
1229
1389
|
author: 'dependabot[bot]',
|
|
@@ -1243,6 +1403,31 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1243
1403
|
allowedIssueAuthors: ['owner'],
|
|
1244
1404
|
});
|
|
1245
1405
|
|
|
1406
|
+
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
1407
|
+
expect(mockIssueRepository.updateStory).not.toHaveBeenCalled();
|
|
1408
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
it('should act on a dependency-update bot only when it is explicitly listed in allowedIssueAuthors', async () => {
|
|
1412
|
+
const pullRequest = createMockPullRequest({
|
|
1413
|
+
status: 'Unread',
|
|
1414
|
+
author: 'dependabot[bot]',
|
|
1415
|
+
});
|
|
1416
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
1417
|
+
issues: [pullRequest],
|
|
1418
|
+
cacheUsed: false,
|
|
1419
|
+
});
|
|
1420
|
+
mockIssueRepository.getOpenPullRequest.mockResolvedValue({
|
|
1421
|
+
...createReadyPr(),
|
|
1422
|
+
isConflicted: true,
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
await useCase.run({
|
|
1426
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1427
|
+
allowIssueCacheMinutes: 10,
|
|
1428
|
+
allowedIssueAuthors: ['owner', 'dependabot[bot]'],
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1246
1431
|
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
1247
1432
|
mockProject,
|
|
1248
1433
|
pullRequest,
|
|
@@ -1254,7 +1439,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1254
1439
|
);
|
|
1255
1440
|
});
|
|
1256
1441
|
|
|
1257
|
-
it('should
|
|
1442
|
+
it('should process no author when allowedIssueAuthors is null', async () => {
|
|
1258
1443
|
const pullRequest = createMockPullRequest({
|
|
1259
1444
|
status: 'Unread',
|
|
1260
1445
|
author: 'outside-contributor',
|
|
@@ -1274,15 +1459,58 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
1274
1459
|
allowedIssueAuthors: null,
|
|
1275
1460
|
});
|
|
1276
1461
|
|
|
1277
|
-
expect(mockIssueRepository.updateStatus).
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1462
|
+
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
1463
|
+
expect(mockIssueRepository.updateStory).not.toHaveBeenCalled();
|
|
1464
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
it('should process no author when allowedIssueAuthors is undefined', async () => {
|
|
1468
|
+
const pullRequest = createMockPullRequest({
|
|
1469
|
+
status: 'Unread',
|
|
1470
|
+
author: 'owner',
|
|
1471
|
+
});
|
|
1472
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
1473
|
+
issues: [pullRequest],
|
|
1474
|
+
cacheUsed: false,
|
|
1475
|
+
});
|
|
1476
|
+
mockIssueRepository.getOpenPullRequest.mockResolvedValue({
|
|
1477
|
+
...createReadyPr(),
|
|
1478
|
+
isConflicted: true,
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
await useCase.run({
|
|
1482
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1483
|
+
allowIssueCacheMinutes: 10,
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
1487
|
+
expect(mockIssueRepository.updateStory).not.toHaveBeenCalled();
|
|
1488
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
it('should process no author when allowedIssueAuthors is an empty list', async () => {
|
|
1492
|
+
const pullRequest = createMockPullRequest({
|
|
1493
|
+
status: 'Unread',
|
|
1494
|
+
author: 'owner',
|
|
1495
|
+
});
|
|
1496
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
1497
|
+
issues: [pullRequest],
|
|
1498
|
+
cacheUsed: false,
|
|
1499
|
+
});
|
|
1500
|
+
mockIssueRepository.getOpenPullRequest.mockResolvedValue({
|
|
1501
|
+
...createReadyPr(),
|
|
1502
|
+
isConflicted: true,
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
await useCase.run({
|
|
1506
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1507
|
+
allowIssueCacheMinutes: 10,
|
|
1508
|
+
allowedIssueAuthors: [],
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
expect(mockIssueRepository.updateStatus).not.toHaveBeenCalled();
|
|
1512
|
+
expect(mockIssueRepository.updateStory).not.toHaveBeenCalled();
|
|
1513
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
1286
1514
|
});
|
|
1287
1515
|
});
|
|
1288
1516
|
});
|