@aaronshaf/ger 2.0.2 → 2.0.4
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/docs/adr/0023-show-reviewer-list.md +42 -0
- package/docs/adr/README.md +1 -0
- package/docs/prd/commands.md +3 -1
- package/docs/prd/data-model.md +19 -8
- package/package.json +1 -1
- package/src/cli/commands/abandon.ts +28 -2
- package/src/cli/commands/add-reviewer.ts +37 -5
- package/src/cli/commands/comment.ts +93 -6
- package/src/cli/commands/comments.ts +34 -2
- package/src/cli/commands/diff.ts +13 -1
- package/src/cli/commands/groups-members.ts +28 -3
- package/src/cli/commands/groups-show.ts +38 -2
- package/src/cli/commands/groups.ts +32 -3
- package/src/cli/commands/incoming.ts +21 -1
- package/src/cli/commands/install-hook.ts +27 -6
- package/src/cli/commands/mine.ts +18 -1
- package/src/cli/commands/projects.ts +22 -2
- package/src/cli/commands/rebase.ts +19 -2
- package/src/cli/commands/remove-reviewer.ts +32 -4
- package/src/cli/commands/restore.ts +15 -1
- package/src/cli/commands/search.ts +22 -1
- package/src/cli/commands/show.ts +114 -1
- package/src/cli/commands/status.ts +11 -1
- package/src/cli/commands/submit.ts +30 -2
- package/src/cli/commands/topic.ts +33 -3
- package/src/cli/commands/vote.ts +15 -1
- package/src/cli/commands/workspace.ts +23 -4
- package/src/cli/register-commands.ts +40 -22
- package/src/cli/register-group-commands.ts +17 -2
- package/src/cli/register-reviewer-commands.ts +16 -2
- package/src/schemas/gerrit.ts +29 -14
- package/src/services/commit-hook.ts +6 -8
- package/tests/show.test.ts +126 -0
- package/tests/submit.test.ts +28 -0
package/tests/show.test.ts
CHANGED
|
@@ -95,6 +95,27 @@ describe('show command', () => {
|
|
|
95
95
|
name: 'John Doe',
|
|
96
96
|
email: 'john@example.com',
|
|
97
97
|
},
|
|
98
|
+
reviewers: {
|
|
99
|
+
REVIEWER: [
|
|
100
|
+
{
|
|
101
|
+
_account_id: 2001,
|
|
102
|
+
name: 'Jane Reviewer',
|
|
103
|
+
email: 'jane.reviewer@example.com',
|
|
104
|
+
username: 'jreviewer',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
email: 'second.reviewer@example.com',
|
|
108
|
+
username: 'sreviewer',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
CC: [
|
|
112
|
+
{
|
|
113
|
+
_account_id: 2003,
|
|
114
|
+
name: 'Team Observer',
|
|
115
|
+
email: 'observer@example.com',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
98
119
|
})
|
|
99
120
|
|
|
100
121
|
const mockDiff = `--- a/src/auth.js
|
|
@@ -194,6 +215,9 @@ describe('show command', () => {
|
|
|
194
215
|
expect(output).toContain('Branch: main')
|
|
195
216
|
expect(output).toContain('Status: NEW')
|
|
196
217
|
expect(output).toContain('Owner: John Doe')
|
|
218
|
+
expect(output).toContain('Reviewers: Jane Reviewer <jane.reviewer@example.com>')
|
|
219
|
+
expect(output).toContain('second.reviewer@example.com')
|
|
220
|
+
expect(output).toContain('CCs: Team Observer <observer@example.com>')
|
|
197
221
|
expect(output).toContain('Change-Id: I123abc456def')
|
|
198
222
|
expect(output).toContain('🔍 Diff:')
|
|
199
223
|
expect(output).toContain('💬 Inline Comments:')
|
|
@@ -234,6 +258,13 @@ describe('show command', () => {
|
|
|
234
258
|
expect(output).toContain('<owner>')
|
|
235
259
|
expect(output).toContain('<name><![CDATA[John Doe]]></name>')
|
|
236
260
|
expect(output).toContain('<email>john@example.com</email>')
|
|
261
|
+
expect(output).toContain('<reviewers>')
|
|
262
|
+
expect(output).toContain('<count>2</count>')
|
|
263
|
+
expect(output).toContain('<name><![CDATA[Jane Reviewer]]></name>')
|
|
264
|
+
expect(output).toContain('<ccs>')
|
|
265
|
+
expect(output).toContain('<count>1</count>')
|
|
266
|
+
expect(output).toContain('<name><![CDATA[Team Observer]]></name>')
|
|
267
|
+
expect(output).not.toContain('<account_id>undefined</account_id>')
|
|
237
268
|
expect(output).toContain('<diff><![CDATA[')
|
|
238
269
|
expect(output).toContain('<comments>')
|
|
239
270
|
expect(output).toContain('<count>3</count>')
|
|
@@ -482,6 +513,14 @@ describe('show command', () => {
|
|
|
482
513
|
expect(parsed.change.branch).toBe('main')
|
|
483
514
|
expect(parsed.change.owner.name).toBe('John Doe')
|
|
484
515
|
expect(parsed.change.owner.email).toBe('john@example.com')
|
|
516
|
+
expect(Array.isArray(parsed.change.reviewers)).toBe(true)
|
|
517
|
+
expect(parsed.change.reviewers.length).toBe(2)
|
|
518
|
+
expect(parsed.change.reviewers[0].name).toBe('Jane Reviewer')
|
|
519
|
+
expect(parsed.change.reviewers[1].email).toBe('second.reviewer@example.com')
|
|
520
|
+
expect(parsed.change.reviewers[1].account_id).toBeUndefined()
|
|
521
|
+
expect(Array.isArray(parsed.change.ccs)).toBe(true)
|
|
522
|
+
expect(parsed.change.ccs.length).toBe(1)
|
|
523
|
+
expect(parsed.change.ccs[0].name).toBe('Team Observer')
|
|
485
524
|
|
|
486
525
|
// Check diff is present
|
|
487
526
|
expect(parsed.diff).toContain('src/auth.js')
|
|
@@ -611,6 +650,93 @@ describe('show command', () => {
|
|
|
611
650
|
expect(parsed.messages[0].revision).toBe(2)
|
|
612
651
|
})
|
|
613
652
|
|
|
653
|
+
test('should fetch reviewers from listChanges when getChange lacks reviewer data', async () => {
|
|
654
|
+
let listChangesOptions: string[] = []
|
|
655
|
+
let listChangesQuery = ''
|
|
656
|
+
|
|
657
|
+
const changeWithoutReviewers = {
|
|
658
|
+
...mockChange,
|
|
659
|
+
reviewers: undefined,
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
server.use(
|
|
663
|
+
http.get('*/a/changes/:changeId', ({ request }) => {
|
|
664
|
+
const url = new URL(request.url)
|
|
665
|
+
if (url.searchParams.get('o') === 'MESSAGES') {
|
|
666
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify({ messages: [] })}`)
|
|
667
|
+
}
|
|
668
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(changeWithoutReviewers)}`)
|
|
669
|
+
}),
|
|
670
|
+
http.get('*/a/changes/', ({ request }) => {
|
|
671
|
+
const url = new URL(request.url)
|
|
672
|
+
listChangesOptions = url.searchParams.getAll('o')
|
|
673
|
+
listChangesQuery = url.searchParams.get('q') || ''
|
|
674
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify([mockChange])}`)
|
|
675
|
+
}),
|
|
676
|
+
http.get('*/a/changes/:changeId/revisions/current/patch', () => {
|
|
677
|
+
return HttpResponse.text(btoa(mockDiff))
|
|
678
|
+
}),
|
|
679
|
+
http.get('*/a/changes/:changeId/revisions/current/comments', () => {
|
|
680
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(mockComments)}`)
|
|
681
|
+
}),
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
const mockConfigLayer = createMockConfigLayer()
|
|
685
|
+
const program = showCommand('12345', {}).pipe(
|
|
686
|
+
Effect.provide(GerritApiServiceLive),
|
|
687
|
+
Effect.provide(mockConfigLayer),
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
await Effect.runPromise(program)
|
|
691
|
+
|
|
692
|
+
expect(listChangesQuery).toBe('change:12345')
|
|
693
|
+
expect(listChangesOptions).toContain('LABELS')
|
|
694
|
+
expect(listChangesOptions).toContain('DETAILED_LABELS')
|
|
695
|
+
expect(listChangesOptions).toContain('DETAILED_ACCOUNTS')
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
test('should not fetch listChanges when reviewer data is explicitly present but empty', async () => {
|
|
699
|
+
let listChangesCalled = false
|
|
700
|
+
|
|
701
|
+
const changeWithEmptyReviewerLists = {
|
|
702
|
+
...mockChange,
|
|
703
|
+
reviewers: {
|
|
704
|
+
REVIEWER: [],
|
|
705
|
+
CC: [],
|
|
706
|
+
},
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
server.use(
|
|
710
|
+
http.get('*/a/changes/:changeId', ({ request }) => {
|
|
711
|
+
const url = new URL(request.url)
|
|
712
|
+
if (url.searchParams.get('o') === 'MESSAGES') {
|
|
713
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify({ messages: [] })}`)
|
|
714
|
+
}
|
|
715
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(changeWithEmptyReviewerLists)}`)
|
|
716
|
+
}),
|
|
717
|
+
http.get('*/a/changes/', () => {
|
|
718
|
+
listChangesCalled = true
|
|
719
|
+
return HttpResponse.text(`)]}'\n[]`)
|
|
720
|
+
}),
|
|
721
|
+
http.get('*/a/changes/:changeId/revisions/current/patch', () => {
|
|
722
|
+
return HttpResponse.text(btoa(mockDiff))
|
|
723
|
+
}),
|
|
724
|
+
http.get('*/a/changes/:changeId/revisions/current/comments', () => {
|
|
725
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(mockComments)}`)
|
|
726
|
+
}),
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
const mockConfigLayer = createMockConfigLayer()
|
|
730
|
+
const program = showCommand('12345', {}).pipe(
|
|
731
|
+
Effect.provide(GerritApiServiceLive),
|
|
732
|
+
Effect.provide(mockConfigLayer),
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
await Effect.runPromise(program)
|
|
736
|
+
|
|
737
|
+
expect(listChangesCalled).toBe(false)
|
|
738
|
+
})
|
|
739
|
+
|
|
614
740
|
test('should handle large JSON output without truncation', async () => {
|
|
615
741
|
// Create a large diff to simulate output > 64KB
|
|
616
742
|
const largeDiff = '--- a/large-file.js\n+++ b/large-file.js\n' + 'x'.repeat(100000)
|
package/tests/submit.test.ts
CHANGED
|
@@ -132,6 +132,34 @@ describe('submit command', () => {
|
|
|
132
132
|
expect(output).toContain('Status: MERGED')
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
+
it('should fetch change without detailed reviewer options', async () => {
|
|
136
|
+
let requestedOptions: string[] = []
|
|
137
|
+
|
|
138
|
+
server.use(
|
|
139
|
+
http.get('*/a/changes/12345', ({ request }) => {
|
|
140
|
+
const url = new URL(request.url)
|
|
141
|
+
requestedOptions = url.searchParams.getAll('o')
|
|
142
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(mockSubmittableChange)}`)
|
|
143
|
+
}),
|
|
144
|
+
http.post('*/a/changes/12345/submit', () => {
|
|
145
|
+
return HttpResponse.text(`)]}'\n${JSON.stringify(mockSubmitResponse)}`)
|
|
146
|
+
}),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService())
|
|
150
|
+
const program = submitCommand('12345', {}).pipe(
|
|
151
|
+
Effect.provide(GerritApiServiceLive),
|
|
152
|
+
Effect.provide(mockConfigLayer),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
await Effect.runPromise(program)
|
|
156
|
+
|
|
157
|
+
expect(requestedOptions).toContain('CURRENT_REVISION')
|
|
158
|
+
expect(requestedOptions).toContain('CURRENT_COMMIT')
|
|
159
|
+
expect(requestedOptions).not.toContain('DETAILED_LABELS')
|
|
160
|
+
expect(requestedOptions).not.toContain('DETAILED_ACCOUNTS')
|
|
161
|
+
})
|
|
162
|
+
|
|
135
163
|
it('should output XML format when --xml flag is used', async () => {
|
|
136
164
|
server.use(
|
|
137
165
|
http.get('*/a/changes/12345', () => {
|