@aaronshaf/ger 2.0.1 → 2.0.3
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/README.md +16 -0
- package/docs/adr/0023-show-reviewer-list.md +42 -0
- package/docs/adr/README.md +1 -0
- package/docs/prd/commands.md +74 -1
- package/docs/prd/data-model.md +19 -8
- package/llms.txt +217 -0
- package/package.json +1 -1
- package/src/api/gerrit.ts +63 -60
- package/src/cli/commands/comment.ts +18 -0
- package/src/cli/commands/install-hook.ts +59 -0
- package/src/cli/commands/show.ts +123 -1
- package/src/cli/commands/topic.ts +108 -0
- package/src/cli/register-commands.ts +55 -33
- package/src/schemas/gerrit.ts +26 -8
- package/tests/show.test.ts +126 -0
- package/tests/submit.test.ts +28 -0
- package/tests/topic.test.ts +443 -0
- package/tests/unit/commands/install-hook.test.ts +258 -0
|
@@ -9,15 +9,17 @@ import { abandonCommand } from './commands/abandon'
|
|
|
9
9
|
import { restoreCommand } from './commands/restore'
|
|
10
10
|
import { rebaseCommand } from './commands/rebase'
|
|
11
11
|
import { submitCommand } from './commands/submit'
|
|
12
|
+
import { topicCommand, TOPIC_HELP_TEXT } from './commands/topic'
|
|
12
13
|
import { voteCommand } from './commands/vote'
|
|
13
14
|
import { projectsCommand } from './commands/projects'
|
|
14
15
|
import { buildStatusCommand, BUILD_STATUS_HELP_TEXT } from './commands/build-status'
|
|
15
16
|
import { checkoutCommand, CHECKOUT_HELP_TEXT } from './commands/checkout'
|
|
16
|
-
import { commentCommand } from './commands/comment'
|
|
17
|
+
import { commentCommand, COMMENT_HELP_TEXT } from './commands/comment'
|
|
17
18
|
import { commentsCommand } from './commands/comments'
|
|
18
19
|
import { diffCommand } from './commands/diff'
|
|
19
20
|
import { extractUrlCommand } from './commands/extract-url'
|
|
20
21
|
import { incomingCommand } from './commands/incoming'
|
|
22
|
+
import { installHookCommand } from './commands/install-hook'
|
|
21
23
|
import { mineCommand } from './commands/mine'
|
|
22
24
|
import { openCommand } from './commands/open'
|
|
23
25
|
import { pushCommand, PUSH_HELP_TEXT } from './commands/push'
|
|
@@ -106,38 +108,7 @@ export function registerCommands(program: Command): void {
|
|
|
106
108
|
.option('--unresolved', 'Mark comment as unresolved (requires human attention)')
|
|
107
109
|
.option('--batch', 'Read batch comments from stdin as JSON (see examples below)')
|
|
108
110
|
.option('--xml', 'XML output for LLM consumption')
|
|
109
|
-
.addHelpText(
|
|
110
|
-
'after',
|
|
111
|
-
`
|
|
112
|
-
Examples:
|
|
113
|
-
# Post a general comment on a change (using change number)
|
|
114
|
-
$ ger comment 12345 -m "Looks good to me!"
|
|
115
|
-
|
|
116
|
-
# Post a comment using Change-ID
|
|
117
|
-
$ ger comment If5a3ae8cb5a107e187447802358417f311d0c4b1 -m "LGTM"
|
|
118
|
-
|
|
119
|
-
# Post a comment using piped input (useful for multi-line comments or scripts)
|
|
120
|
-
$ echo "This is a comment from stdin!" | ger comment 12345
|
|
121
|
-
$ cat review-notes.txt | ger comment 12345
|
|
122
|
-
|
|
123
|
-
# Post a line-specific comment (line number from NEW file version)
|
|
124
|
-
$ ger comment 12345 --file src/main.js --line 42 -m "Consider using const here"
|
|
125
|
-
|
|
126
|
-
# Post an unresolved comment requiring human attention
|
|
127
|
-
$ ger comment 12345 --file src/api.js --line 15 -m "Security concern" --unresolved
|
|
128
|
-
|
|
129
|
-
# Post multiple comments using batch mode
|
|
130
|
-
$ echo '{"message": "Review complete", "comments": [
|
|
131
|
-
{"file": "src/main.js", "line": 10, "message": "Good refactor"},
|
|
132
|
-
{"file": "src/api.js", "line": 25, "message": "Check error handling", "unresolved": true}
|
|
133
|
-
]}' | ger comment 12345 --batch
|
|
134
|
-
|
|
135
|
-
Note:
|
|
136
|
-
- Both change number (e.g., 12345) and Change-ID (e.g., If5a3ae8...) formats are accepted
|
|
137
|
-
- Line numbers refer to the actual line numbers in the NEW version of the file,
|
|
138
|
-
NOT the line numbers shown in the diff view. To find the correct line number,
|
|
139
|
-
look at the file after all changes have been applied.`,
|
|
140
|
-
)
|
|
111
|
+
.addHelpText('after', COMMENT_HELP_TEXT)
|
|
141
112
|
.action(async (changeId, options) => {
|
|
142
113
|
await executeEffect(
|
|
143
114
|
commentCommand(changeId, options).pipe(
|
|
@@ -316,6 +287,24 @@ Note:
|
|
|
316
287
|
)
|
|
317
288
|
})
|
|
318
289
|
|
|
290
|
+
// topic command
|
|
291
|
+
program
|
|
292
|
+
.command('topic [change-id] [topic]')
|
|
293
|
+
.description('Get, set, or remove topic for a change (auto-detects from HEAD if not specified)')
|
|
294
|
+
.option('--delete', 'Remove the topic from the change')
|
|
295
|
+
.option('--xml', 'XML output for LLM consumption')
|
|
296
|
+
.addHelpText('after', TOPIC_HELP_TEXT)
|
|
297
|
+
.action(async (changeId, topic, options) => {
|
|
298
|
+
await executeEffect(
|
|
299
|
+
topicCommand(changeId, topic, options).pipe(
|
|
300
|
+
Effect.provide(GerritApiServiceLive),
|
|
301
|
+
Effect.provide(ConfigServiceLive),
|
|
302
|
+
),
|
|
303
|
+
options,
|
|
304
|
+
'topic_result',
|
|
305
|
+
)
|
|
306
|
+
})
|
|
307
|
+
|
|
319
308
|
// vote command
|
|
320
309
|
program
|
|
321
310
|
.command('vote <change-id>')
|
|
@@ -528,6 +517,39 @@ Note:
|
|
|
528
517
|
}
|
|
529
518
|
})
|
|
530
519
|
|
|
520
|
+
// install-hook command
|
|
521
|
+
program
|
|
522
|
+
.command('install-hook')
|
|
523
|
+
.description('Install the Gerrit commit-msg hook for automatic Change-Id generation')
|
|
524
|
+
.option('--force', 'Overwrite existing hook')
|
|
525
|
+
.option('--xml', 'XML output for LLM consumption')
|
|
526
|
+
.addHelpText(
|
|
527
|
+
'after',
|
|
528
|
+
`
|
|
529
|
+
Examples:
|
|
530
|
+
# Install the commit-msg hook
|
|
531
|
+
$ ger install-hook
|
|
532
|
+
|
|
533
|
+
# Force reinstall (overwrite existing)
|
|
534
|
+
$ ger install-hook --force
|
|
535
|
+
|
|
536
|
+
Note:
|
|
537
|
+
- Downloads hook from your configured Gerrit server
|
|
538
|
+
- Installs to .git/hooks/commit-msg
|
|
539
|
+
- Makes hook executable (chmod +x)
|
|
540
|
+
- Required for commits to have Change-Id footers`,
|
|
541
|
+
)
|
|
542
|
+
.action(async (options) => {
|
|
543
|
+
await executeEffect(
|
|
544
|
+
installHookCommand(options).pipe(
|
|
545
|
+
Effect.provide(CommitHookServiceLive),
|
|
546
|
+
Effect.provide(ConfigServiceLive),
|
|
547
|
+
),
|
|
548
|
+
options,
|
|
549
|
+
'install_hook_result',
|
|
550
|
+
)
|
|
551
|
+
})
|
|
552
|
+
|
|
531
553
|
// push command
|
|
532
554
|
program
|
|
533
555
|
.command('push')
|
package/src/schemas/gerrit.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Schema } from '@effect/schema'
|
|
2
2
|
|
|
3
|
-
// Authentication schemas
|
|
4
3
|
export const GerritCredentials: Schema.Schema<{
|
|
5
4
|
readonly host: string
|
|
6
5
|
readonly username: string
|
|
@@ -21,7 +20,6 @@ export const GerritCredentials: Schema.Schema<{
|
|
|
21
20
|
})
|
|
22
21
|
export type GerritCredentials = Schema.Schema.Type<typeof GerritCredentials>
|
|
23
22
|
|
|
24
|
-
// Forward declare RevisionInfo type for use in ChangeInfo
|
|
25
23
|
export interface RevisionInfoType {
|
|
26
24
|
readonly kind?: string
|
|
27
25
|
readonly _number: number
|
|
@@ -65,7 +63,22 @@ export interface RevisionInfoType {
|
|
|
65
63
|
>
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
type ChangeReviewerAccount = {
|
|
67
|
+
readonly _account_id?: number
|
|
68
|
+
readonly name?: string
|
|
69
|
+
readonly email?: string
|
|
70
|
+
readonly username?: string
|
|
71
|
+
}
|
|
72
|
+
const ChangeReviewerAccountInfo: Schema.Schema<ChangeReviewerAccount> = Schema.Struct({
|
|
73
|
+
_account_id: Schema.optional(Schema.Number),
|
|
74
|
+
name: Schema.optional(Schema.String),
|
|
75
|
+
email: Schema.optional(Schema.String),
|
|
76
|
+
username: Schema.optional(Schema.String),
|
|
77
|
+
})
|
|
78
|
+
type ChangeReviewerMap = Partial<
|
|
79
|
+
Record<'REVIEWER' | 'CC' | 'REMOVED', ReadonlyArray<ChangeReviewerAccount>>
|
|
80
|
+
>
|
|
81
|
+
|
|
69
82
|
export const ChangeInfo: Schema.Schema<{
|
|
70
83
|
readonly id: string
|
|
71
84
|
readonly project: string
|
|
@@ -118,6 +131,8 @@ export const ChangeInfo: Schema.Schema<{
|
|
|
118
131
|
readonly work_in_progress?: boolean
|
|
119
132
|
readonly current_revision?: string
|
|
120
133
|
readonly revisions?: Record<string, RevisionInfoType>
|
|
134
|
+
readonly topic?: string
|
|
135
|
+
readonly reviewers?: ChangeReviewerMap
|
|
121
136
|
}> = Schema.Struct({
|
|
122
137
|
id: Schema.String,
|
|
123
138
|
project: Schema.String,
|
|
@@ -182,10 +197,17 @@ export const ChangeInfo: Schema.Schema<{
|
|
|
182
197
|
work_in_progress: Schema.optional(Schema.Boolean),
|
|
183
198
|
current_revision: Schema.optional(Schema.String),
|
|
184
199
|
revisions: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
200
|
+
topic: Schema.optional(Schema.String),
|
|
201
|
+
reviewers: Schema.optional(
|
|
202
|
+
Schema.Struct({
|
|
203
|
+
REVIEWER: Schema.optional(Schema.Array(ChangeReviewerAccountInfo)),
|
|
204
|
+
CC: Schema.optional(Schema.Array(ChangeReviewerAccountInfo)),
|
|
205
|
+
REMOVED: Schema.optional(Schema.Array(ChangeReviewerAccountInfo)),
|
|
206
|
+
}),
|
|
207
|
+
),
|
|
185
208
|
})
|
|
186
209
|
export type ChangeInfo = Schema.Schema.Type<typeof ChangeInfo>
|
|
187
210
|
|
|
188
|
-
// Comment schemas
|
|
189
211
|
export const CommentInput: Schema.Schema<{
|
|
190
212
|
readonly message: string
|
|
191
213
|
readonly unresolved?: boolean
|
|
@@ -198,7 +220,6 @@ export const CommentInput: Schema.Schema<{
|
|
|
198
220
|
})
|
|
199
221
|
export type CommentInput = Schema.Schema.Type<typeof CommentInput>
|
|
200
222
|
|
|
201
|
-
// Comment info returned from API
|
|
202
223
|
export const CommentInfo: Schema.Schema<{
|
|
203
224
|
readonly id: string
|
|
204
225
|
readonly path?: string
|
|
@@ -244,7 +265,6 @@ export const CommentInfo: Schema.Schema<{
|
|
|
244
265
|
})
|
|
245
266
|
export type CommentInfo = Schema.Schema.Type<typeof CommentInfo>
|
|
246
267
|
|
|
247
|
-
// Message info for review messages
|
|
248
268
|
export const MessageInfo: Schema.Schema<{
|
|
249
269
|
readonly id: string
|
|
250
270
|
readonly message: string
|
|
@@ -317,7 +337,6 @@ export const ReviewInput: Schema.Schema<{
|
|
|
317
337
|
})
|
|
318
338
|
export type ReviewInput = Schema.Schema.Type<typeof ReviewInput>
|
|
319
339
|
|
|
320
|
-
// Project schema
|
|
321
340
|
export const ProjectInfo: Schema.Schema<{
|
|
322
341
|
readonly id: string
|
|
323
342
|
readonly name: string
|
|
@@ -331,7 +350,6 @@ export const ProjectInfo: Schema.Schema<{
|
|
|
331
350
|
})
|
|
332
351
|
export type ProjectInfo = Schema.Schema.Type<typeof ProjectInfo>
|
|
333
352
|
|
|
334
|
-
// File and diff schemas
|
|
335
353
|
export const FileInfo: Schema.Schema<{
|
|
336
354
|
readonly status?: 'A' | 'D' | 'R' | 'C' | 'M'
|
|
337
355
|
readonly lines_inserted?: number
|
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', () => {
|