@aaronshaf/ger 1.2.11 → 2.0.0
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/.ast-grep/rules/no-as-casting.yml +13 -0
- package/.claude-plugin/plugin.json +22 -0
- package/.github/workflows/ci-simple.yml +53 -0
- package/.github/workflows/ci.yml +171 -0
- package/.github/workflows/claude-code-review.yml +83 -0
- package/.github/workflows/claude.yml +50 -0
- package/.github/workflows/dependency-update.yml +84 -0
- package/.github/workflows/release.yml +166 -0
- package/.github/workflows/security-scan.yml +113 -0
- package/.github/workflows/security.yml +96 -0
- package/.husky/pre-commit +16 -0
- package/.husky/pre-push +25 -0
- package/.lintstagedrc.json +6 -0
- package/.tool-versions +1 -0
- package/CLAUDE.md +105 -0
- package/DEVELOPMENT.md +361 -0
- package/EXAMPLES.md +457 -0
- package/README.md +831 -16
- package/bin/ger +3 -18
- package/biome.json +36 -0
- package/bun.lock +678 -0
- package/bunfig.toml +8 -0
- package/docs/adr/0001-use-effect-for-side-effects.md +65 -0
- package/docs/adr/0002-use-bun-runtime.md +64 -0
- package/docs/adr/0003-store-credentials-in-home-directory.md +75 -0
- package/docs/adr/0004-use-commander-for-cli.md +76 -0
- package/docs/adr/0005-use-effect-schema-for-validation.md +93 -0
- package/docs/adr/0006-use-msw-for-api-mocking.md +89 -0
- package/docs/adr/0007-git-hooks-for-quality.md +94 -0
- package/docs/adr/0008-no-as-typecasting.md +83 -0
- package/docs/adr/0009-file-size-limits.md +82 -0
- package/docs/adr/0010-llm-friendly-xml-output.md +93 -0
- package/docs/adr/0011-ai-tool-strategy-pattern.md +102 -0
- package/docs/adr/0012-build-status-message-parsing.md +94 -0
- package/docs/adr/0013-git-subprocess-integration.md +98 -0
- package/docs/adr/0014-group-management-support.md +95 -0
- package/docs/adr/0015-batch-comment-processing.md +111 -0
- package/docs/adr/0016-flexible-change-identifiers.md +94 -0
- package/docs/adr/0017-git-worktree-support.md +102 -0
- package/docs/adr/0018-auto-install-commit-hook.md +103 -0
- package/docs/adr/0019-sdk-package-exports.md +95 -0
- package/docs/adr/0020-code-coverage-enforcement.md +105 -0
- package/docs/adr/0021-typescript-isolated-declarations.md +83 -0
- package/docs/adr/0022-biome-oxlint-tooling.md +124 -0
- package/docs/adr/README.md +30 -0
- package/docs/prd/README.md +12 -0
- package/docs/prd/architecture.md +325 -0
- package/docs/prd/commands.md +425 -0
- package/docs/prd/data-model.md +349 -0
- package/docs/prd/overview.md +124 -0
- package/index.ts +219 -0
- package/oxlint.json +24 -0
- package/package.json +82 -15
- package/scripts/check-coverage.ts +69 -0
- package/scripts/check-file-size.ts +38 -0
- package/scripts/fix-test-mocks.ts +55 -0
- package/skills/gerrit-workflow/SKILL.md +247 -0
- package/skills/gerrit-workflow/examples.md +572 -0
- package/skills/gerrit-workflow/reference.md +728 -0
- package/src/api/gerrit.ts +696 -0
- package/src/cli/commands/abandon.ts +65 -0
- package/src/cli/commands/add-reviewer.ts +156 -0
- package/src/cli/commands/build-status.ts +282 -0
- package/src/cli/commands/checkout.ts +422 -0
- package/src/cli/commands/comment.ts +460 -0
- package/src/cli/commands/comments.ts +85 -0
- package/src/cli/commands/diff.ts +71 -0
- package/src/cli/commands/extract-url.ts +266 -0
- package/src/cli/commands/groups-members.ts +104 -0
- package/src/cli/commands/groups-show.ts +169 -0
- package/src/cli/commands/groups.ts +137 -0
- package/src/cli/commands/incoming.ts +226 -0
- package/src/cli/commands/init.ts +164 -0
- package/src/cli/commands/mine.ts +115 -0
- package/src/cli/commands/open.ts +57 -0
- package/src/cli/commands/projects.ts +68 -0
- package/src/cli/commands/push.ts +430 -0
- package/src/cli/commands/rebase.ts +52 -0
- package/src/cli/commands/remove-reviewer.ts +123 -0
- package/src/cli/commands/restore.ts +50 -0
- package/src/cli/commands/review.ts +486 -0
- package/src/cli/commands/search.ts +162 -0
- package/src/cli/commands/setup.ts +286 -0
- package/src/cli/commands/show.ts +491 -0
- package/src/cli/commands/status.ts +35 -0
- package/src/cli/commands/submit.ts +108 -0
- package/src/cli/commands/vote.ts +119 -0
- package/src/cli/commands/workspace.ts +200 -0
- package/src/cli/index.ts +53 -0
- package/src/cli/register-commands.ts +659 -0
- package/src/cli/register-group-commands.ts +88 -0
- package/src/cli/register-reviewer-commands.ts +97 -0
- package/src/prompts/default-review.md +86 -0
- package/src/prompts/system-inline-review.md +135 -0
- package/src/prompts/system-overall-review.md +206 -0
- package/src/schemas/config.test.ts +245 -0
- package/src/schemas/config.ts +84 -0
- package/src/schemas/gerrit.ts +681 -0
- package/src/services/commit-hook.ts +314 -0
- package/src/services/config.test.ts +150 -0
- package/src/services/config.ts +250 -0
- package/src/services/git-worktree.ts +342 -0
- package/src/services/review-strategy.ts +292 -0
- package/src/test-utils/mock-generator.ts +138 -0
- package/src/utils/change-id.test.ts +98 -0
- package/src/utils/change-id.ts +63 -0
- package/src/utils/comment-formatters.ts +153 -0
- package/src/utils/diff-context.ts +103 -0
- package/src/utils/diff-formatters.ts +141 -0
- package/src/utils/formatters.ts +85 -0
- package/src/utils/git-commit.test.ts +277 -0
- package/src/utils/git-commit.ts +122 -0
- package/src/utils/index.ts +55 -0
- package/src/utils/message-filters.ts +26 -0
- package/src/utils/review-formatters.ts +89 -0
- package/src/utils/review-prompt-builder.ts +110 -0
- package/src/utils/shell-safety.ts +117 -0
- package/src/utils/status-indicators.ts +100 -0
- package/src/utils/url-parser.test.ts +271 -0
- package/src/utils/url-parser.ts +118 -0
- package/tests/abandon.test.ts +230 -0
- package/tests/add-reviewer.test.ts +579 -0
- package/tests/build-status-watch.test.ts +344 -0
- package/tests/build-status.test.ts +789 -0
- package/tests/change-id-formats.test.ts +268 -0
- package/tests/checkout/integration.test.ts +653 -0
- package/tests/checkout/parse-input.test.ts +55 -0
- package/tests/checkout/validation.test.ts +178 -0
- package/tests/comment-batch-advanced.test.ts +431 -0
- package/tests/comment-gerrit-api-compliance.test.ts +414 -0
- package/tests/comment.test.ts +708 -0
- package/tests/comments.test.ts +323 -0
- package/tests/config-service-simple.test.ts +100 -0
- package/tests/diff.test.ts +419 -0
- package/tests/extract-url.test.ts +517 -0
- package/tests/groups-members.test.ts +256 -0
- package/tests/groups-show.test.ts +323 -0
- package/tests/groups.test.ts +334 -0
- package/tests/helpers/build-status-test-setup.ts +83 -0
- package/tests/helpers/config-mock.ts +27 -0
- package/tests/incoming.test.ts +357 -0
- package/tests/init.test.ts +70 -0
- package/tests/integration/commit-hook.test.ts +246 -0
- package/tests/interactive-incoming.test.ts +173 -0
- package/tests/mine.test.ts +285 -0
- package/tests/mocks/msw-handlers.ts +80 -0
- package/tests/open.test.ts +233 -0
- package/tests/projects.test.ts +259 -0
- package/tests/rebase.test.ts +271 -0
- package/tests/remove-reviewer.test.ts +357 -0
- package/tests/restore.test.ts +237 -0
- package/tests/review.test.ts +135 -0
- package/tests/search.test.ts +712 -0
- package/tests/setup.test.ts +63 -0
- package/tests/show-auto-detect.test.ts +324 -0
- package/tests/show.test.ts +813 -0
- package/tests/status.test.ts +145 -0
- package/tests/submit.test.ts +316 -0
- package/tests/unit/commands/push.test.ts +194 -0
- package/tests/unit/git-branch-detection.test.ts +82 -0
- package/tests/unit/git-worktree.test.ts +55 -0
- package/tests/unit/patterns/push-patterns.test.ts +148 -0
- package/tests/unit/schemas/gerrit.test.ts +85 -0
- package/tests/unit/services/commit-hook.test.ts +132 -0
- package/tests/unit/services/review-strategy.test.ts +349 -0
- package/tests/unit/test-utils/mock-generator.test.ts +154 -0
- package/tests/unit/utils/comment-formatters.test.ts +415 -0
- package/tests/unit/utils/diff-context.test.ts +171 -0
- package/tests/unit/utils/diff-formatters.test.ts +165 -0
- package/tests/unit/utils/formatters.test.ts +411 -0
- package/tests/unit/utils/message-filters.test.ts +227 -0
- package/tests/unit/utils/shell-safety.test.ts +230 -0
- package/tests/unit/utils/status-indicators.test.ts +137 -0
- package/tests/vote.test.ts +317 -0
- package/tests/workspace.test.ts +295 -0
- package/tsconfig.json +36 -5
- package/src/commands/branch.ts +0 -196
- package/src/ger.ts +0 -22
- package/src/types.d.ts +0 -35
- package/src/utils.ts +0 -130
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import { normalizeGerritHost } from '@/utils/url-parser'
|
|
3
|
+
|
|
4
|
+
describe('Setup Command', () => {
|
|
5
|
+
describe('URL normalization integration', () => {
|
|
6
|
+
test('should normalize host URL using normalizeGerritHost', () => {
|
|
7
|
+
// Test that the utility function is working as expected
|
|
8
|
+
expect(normalizeGerritHost('gerrit.example.com')).toBe('https://gerrit.example.com')
|
|
9
|
+
expect(normalizeGerritHost('https://gerrit.example.com/')).toBe('https://gerrit.example.com')
|
|
10
|
+
expect(normalizeGerritHost('gerrit.example.com:8080')).toBe('https://gerrit.example.com:8080')
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('Configuration validation', () => {
|
|
15
|
+
test('should validate required fields', () => {
|
|
16
|
+
const config = {
|
|
17
|
+
host: 'https://gerrit.example.com',
|
|
18
|
+
username: 'testuser',
|
|
19
|
+
password: 'testpass',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
expect(config.host).toBeTruthy()
|
|
23
|
+
expect(config.username).toBeTruthy()
|
|
24
|
+
expect(config.password).toBeTruthy()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('should reject empty required fields', () => {
|
|
28
|
+
const config = {
|
|
29
|
+
host: '',
|
|
30
|
+
username: 'testuser',
|
|
31
|
+
password: 'testpass',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
expect(config.host).toBeFalsy()
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('AI tool detection', () => {
|
|
39
|
+
test('should check for available tools', () => {
|
|
40
|
+
const availableTools = ['claude', 'llm', 'chatgpt']
|
|
41
|
+
expect(availableTools).toContain('claude')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('should handle missing tools', () => {
|
|
45
|
+
const availableTools: string[] = []
|
|
46
|
+
expect(availableTools).not.toContain('nonexistent-tool')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('Connection verification', () => {
|
|
51
|
+
test('should test connection success scenario', () => {
|
|
52
|
+
const mockResponse = { ok: true, status: 200 }
|
|
53
|
+
expect(mockResponse.ok).toBe(true)
|
|
54
|
+
expect(mockResponse.status).toBe(200)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should handle connection failures', () => {
|
|
58
|
+
const mockResponse = { ok: false, status: 401 }
|
|
59
|
+
expect(mockResponse.ok).toBe(false)
|
|
60
|
+
expect(mockResponse.status).toBe(401)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll, afterEach, mock, spyOn } from 'bun:test'
|
|
2
|
+
import { setupServer } from 'msw/node'
|
|
3
|
+
import { http, HttpResponse } from 'msw'
|
|
4
|
+
import { Effect, Layer } from 'effect'
|
|
5
|
+
import { showCommand } from '@/cli/commands/show'
|
|
6
|
+
import { GerritApiServiceLive } from '@/api/gerrit'
|
|
7
|
+
import { ConfigService } from '@/services/config'
|
|
8
|
+
import { generateMockChange } from '@/test-utils/mock-generator'
|
|
9
|
+
import { createMockConfigService } from './helpers/config-mock'
|
|
10
|
+
import * as childProcess from 'node:child_process'
|
|
11
|
+
import { EventEmitter } from 'node:events'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Integration tests for auto-detecting Change-ID from HEAD commit
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const mockChange = generateMockChange({
|
|
18
|
+
_number: 392385,
|
|
19
|
+
change_id: 'If5a3ae8cb5a107e187447802358417f311d0c4b1',
|
|
20
|
+
subject: 'WIP: test',
|
|
21
|
+
status: 'NEW',
|
|
22
|
+
project: 'my-project',
|
|
23
|
+
branch: 'master',
|
|
24
|
+
created: '2024-01-15 10:00:00.000000000',
|
|
25
|
+
updated: '2024-01-15 12:00:00.000000000',
|
|
26
|
+
owner: {
|
|
27
|
+
_account_id: 1001,
|
|
28
|
+
name: 'Test User',
|
|
29
|
+
email: 'test@example.com',
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const mockDiff = `--- a/test.txt
|
|
34
|
+
+++ b/test.txt
|
|
35
|
+
@@ -1,1 +1,2 @@
|
|
36
|
+
original line
|
|
37
|
+
+new line`
|
|
38
|
+
|
|
39
|
+
const server = setupServer(
|
|
40
|
+
http.get('*/a/accounts/self', () => {
|
|
41
|
+
return HttpResponse.json({
|
|
42
|
+
_account_id: 1000,
|
|
43
|
+
name: 'Test User',
|
|
44
|
+
email: 'test@example.com',
|
|
45
|
+
})
|
|
46
|
+
}),
|
|
47
|
+
|
|
48
|
+
// Handler that matches the auto-detected Change-ID
|
|
49
|
+
http.get('*/a/changes/:changeId', ({ params }) => {
|
|
50
|
+
const { changeId } = params
|
|
51
|
+
if (changeId === 'If5a3ae8cb5a107e187447802358417f311d0c4b1') {
|
|
52
|
+
return HttpResponse.text(`)]}'
|
|
53
|
+
${JSON.stringify(mockChange)}`)
|
|
54
|
+
}
|
|
55
|
+
return HttpResponse.text('Not Found', { status: 404 })
|
|
56
|
+
}),
|
|
57
|
+
|
|
58
|
+
http.get('*/a/changes/:changeId/revisions/current/patch', ({ params }) => {
|
|
59
|
+
const { changeId } = params
|
|
60
|
+
if (changeId === 'If5a3ae8cb5a107e187447802358417f311d0c4b1') {
|
|
61
|
+
return HttpResponse.text(btoa(mockDiff))
|
|
62
|
+
}
|
|
63
|
+
return HttpResponse.text('Not Found', { status: 404 })
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
http.get('*/a/changes/:changeId/revisions/current/comments', ({ params }) => {
|
|
67
|
+
const { changeId } = params
|
|
68
|
+
if (changeId === 'If5a3ae8cb5a107e187447802358417f311d0c4b1') {
|
|
69
|
+
return HttpResponse.text(`)]}'
|
|
70
|
+
{}`)
|
|
71
|
+
}
|
|
72
|
+
return HttpResponse.text('Not Found', { status: 404 })
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
let capturedLogs: string[] = []
|
|
77
|
+
let capturedErrors: string[] = []
|
|
78
|
+
let capturedStdout: string[] = []
|
|
79
|
+
|
|
80
|
+
const mockConsoleLog = mock((...args: any[]) => {
|
|
81
|
+
capturedLogs.push(args.join(' '))
|
|
82
|
+
})
|
|
83
|
+
const mockConsoleError = mock((...args: any[]) => {
|
|
84
|
+
capturedErrors.push(args.join(' '))
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Mock process.stdout.write to capture JSON/XML output and handle callbacks
|
|
88
|
+
const mockStdoutWrite = mock((chunk: any, callback?: any) => {
|
|
89
|
+
capturedStdout.push(String(chunk))
|
|
90
|
+
// Call the callback synchronously if provided
|
|
91
|
+
if (typeof callback === 'function') {
|
|
92
|
+
callback()
|
|
93
|
+
}
|
|
94
|
+
return true
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const originalConsoleLog = console.log
|
|
98
|
+
const originalConsoleError = console.error
|
|
99
|
+
const originalStdoutWrite = process.stdout.write
|
|
100
|
+
|
|
101
|
+
let spawnSpy: ReturnType<typeof spyOn>
|
|
102
|
+
|
|
103
|
+
beforeAll(() => {
|
|
104
|
+
server.listen({ onUnhandledRequest: 'bypass' })
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
console.log = mockConsoleLog
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
console.error = mockConsoleError
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
process.stdout.write = mockStdoutWrite
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
afterAll(() => {
|
|
114
|
+
server.close()
|
|
115
|
+
console.log = originalConsoleLog
|
|
116
|
+
console.error = originalConsoleError
|
|
117
|
+
// @ts-ignore
|
|
118
|
+
process.stdout.write = originalStdoutWrite
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
afterEach(() => {
|
|
122
|
+
server.resetHandlers()
|
|
123
|
+
mockConsoleLog.mockClear()
|
|
124
|
+
mockConsoleError.mockClear()
|
|
125
|
+
mockStdoutWrite.mockClear()
|
|
126
|
+
capturedLogs = []
|
|
127
|
+
capturedErrors = []
|
|
128
|
+
capturedStdout = []
|
|
129
|
+
|
|
130
|
+
if (spawnSpy) {
|
|
131
|
+
spawnSpy.mockRestore()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const createMockConfigLayer = (): Layer.Layer<ConfigService, never, never> =>
|
|
136
|
+
Layer.succeed(ConfigService, createMockConfigService())
|
|
137
|
+
|
|
138
|
+
describe('show command with auto-detection', () => {
|
|
139
|
+
test('auto-detects Change-ID from HEAD commit when no argument provided', async () => {
|
|
140
|
+
const commitMessage = `feat: add feature
|
|
141
|
+
|
|
142
|
+
Change-Id: If5a3ae8cb5a107e187447802358417f311d0c4b1`
|
|
143
|
+
|
|
144
|
+
// Mock git log command
|
|
145
|
+
const mockChildProcess = new EventEmitter()
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
mockChildProcess.stdout = new EventEmitter()
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
mockChildProcess.stderr = new EventEmitter()
|
|
150
|
+
|
|
151
|
+
spawnSpy = spyOn(childProcess, 'spawn')
|
|
152
|
+
spawnSpy.mockReturnValue(mockChildProcess as any)
|
|
153
|
+
|
|
154
|
+
const effect = showCommand(undefined, {}).pipe(
|
|
155
|
+
Effect.provide(GerritApiServiceLive),
|
|
156
|
+
Effect.provide(createMockConfigLayer()),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const resultPromise = Effect.runPromise(effect)
|
|
160
|
+
|
|
161
|
+
// Simulate git log success
|
|
162
|
+
setImmediate(() => {
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
mockChildProcess.stdout.emit('data', Buffer.from(commitMessage))
|
|
165
|
+
mockChildProcess.emit('close', 0)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
await resultPromise
|
|
169
|
+
|
|
170
|
+
const output = capturedLogs.join('\n')
|
|
171
|
+
expect(output).toContain('Change 392385')
|
|
172
|
+
expect(output).toContain('WIP: test')
|
|
173
|
+
expect(capturedErrors.length).toBe(0)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('auto-detects Change-ID with --xml flag', async () => {
|
|
177
|
+
const commitMessage = `feat: add feature
|
|
178
|
+
|
|
179
|
+
Change-Id: If5a3ae8cb5a107e187447802358417f311d0c4b1`
|
|
180
|
+
|
|
181
|
+
const mockChildProcess = new EventEmitter()
|
|
182
|
+
// @ts-ignore
|
|
183
|
+
mockChildProcess.stdout = new EventEmitter()
|
|
184
|
+
// @ts-ignore
|
|
185
|
+
mockChildProcess.stderr = new EventEmitter()
|
|
186
|
+
|
|
187
|
+
spawnSpy = spyOn(childProcess, 'spawn')
|
|
188
|
+
spawnSpy.mockReturnValue(mockChildProcess as any)
|
|
189
|
+
|
|
190
|
+
const effect = showCommand(undefined, { xml: true }).pipe(
|
|
191
|
+
Effect.provide(GerritApiServiceLive),
|
|
192
|
+
Effect.provide(createMockConfigLayer()),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
const resultPromise = Effect.runPromise(effect)
|
|
196
|
+
|
|
197
|
+
setImmediate(() => {
|
|
198
|
+
// @ts-ignore
|
|
199
|
+
mockChildProcess.stdout.emit('data', Buffer.from(commitMessage))
|
|
200
|
+
mockChildProcess.emit('close', 0)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
await resultPromise
|
|
204
|
+
|
|
205
|
+
const output = capturedStdout.join('')
|
|
206
|
+
expect(output).toContain('<?xml version="1.0" encoding="UTF-8"?>')
|
|
207
|
+
expect(output).toContain('<show_result>')
|
|
208
|
+
expect(output).toContain('<status>success</status>')
|
|
209
|
+
expect(output).toContain('392385')
|
|
210
|
+
expect(capturedErrors.length).toBe(0)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('shows error when no Change-ID in HEAD commit', async () => {
|
|
214
|
+
const commitMessage = `feat: add feature without Change-ID
|
|
215
|
+
|
|
216
|
+
This commit has no Change-ID footer.`
|
|
217
|
+
|
|
218
|
+
const mockChildProcess = new EventEmitter()
|
|
219
|
+
// @ts-ignore
|
|
220
|
+
mockChildProcess.stdout = new EventEmitter()
|
|
221
|
+
// @ts-ignore
|
|
222
|
+
mockChildProcess.stderr = new EventEmitter()
|
|
223
|
+
|
|
224
|
+
spawnSpy = spyOn(childProcess, 'spawn')
|
|
225
|
+
spawnSpy.mockReturnValue(mockChildProcess as any)
|
|
226
|
+
|
|
227
|
+
const effect = showCommand(undefined, {}).pipe(
|
|
228
|
+
Effect.provide(GerritApiServiceLive),
|
|
229
|
+
Effect.provide(createMockConfigLayer()),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
const resultPromise = Effect.runPromise(effect)
|
|
233
|
+
|
|
234
|
+
setImmediate(() => {
|
|
235
|
+
// @ts-ignore
|
|
236
|
+
mockChildProcess.stdout.emit('data', Buffer.from(commitMessage))
|
|
237
|
+
mockChildProcess.emit('close', 0)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await resultPromise
|
|
241
|
+
|
|
242
|
+
const output = capturedErrors.join('\n')
|
|
243
|
+
expect(output).toContain('No Change-ID found in HEAD commit')
|
|
244
|
+
expect(capturedLogs.length).toBe(0)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test('shows error when not in git repository', async () => {
|
|
248
|
+
const mockChildProcess = new EventEmitter()
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
mockChildProcess.stdout = new EventEmitter()
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
mockChildProcess.stderr = new EventEmitter()
|
|
253
|
+
|
|
254
|
+
spawnSpy = spyOn(childProcess, 'spawn')
|
|
255
|
+
spawnSpy.mockReturnValue(mockChildProcess as any)
|
|
256
|
+
|
|
257
|
+
const effect = showCommand(undefined, {}).pipe(
|
|
258
|
+
Effect.provide(GerritApiServiceLive),
|
|
259
|
+
Effect.provide(createMockConfigLayer()),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
const resultPromise = Effect.runPromise(effect)
|
|
263
|
+
|
|
264
|
+
setImmediate(() => {
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
mockChildProcess.stderr.emit('data', Buffer.from('fatal: not a git repository'))
|
|
267
|
+
mockChildProcess.emit('close', 128)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
await resultPromise
|
|
271
|
+
|
|
272
|
+
const output = capturedErrors.join('\n')
|
|
273
|
+
expect(output).toContain('fatal: not a git repository')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
test('still works with explicit change-id argument', async () => {
|
|
277
|
+
// Don't mock git - should not be called when changeId is provided
|
|
278
|
+
const effect = showCommand('If5a3ae8cb5a107e187447802358417f311d0c4b1', {}).pipe(
|
|
279
|
+
Effect.provide(GerritApiServiceLive),
|
|
280
|
+
Effect.provide(createMockConfigLayer()),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
await Effect.runPromise(effect)
|
|
284
|
+
|
|
285
|
+
const output = capturedLogs.join('\n')
|
|
286
|
+
expect(output).toContain('Change 392385')
|
|
287
|
+
expect(output).toContain('WIP: test')
|
|
288
|
+
expect(capturedErrors.length).toBe(0)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test('shows XML error when no Change-ID in commit with --xml flag', async () => {
|
|
292
|
+
const commitMessage = `feat: no change id`
|
|
293
|
+
|
|
294
|
+
const mockChildProcess = new EventEmitter()
|
|
295
|
+
// @ts-ignore
|
|
296
|
+
mockChildProcess.stdout = new EventEmitter()
|
|
297
|
+
// @ts-ignore
|
|
298
|
+
mockChildProcess.stderr = new EventEmitter()
|
|
299
|
+
|
|
300
|
+
spawnSpy = spyOn(childProcess, 'spawn')
|
|
301
|
+
spawnSpy.mockReturnValue(mockChildProcess as any)
|
|
302
|
+
|
|
303
|
+
const effect = showCommand(undefined, { xml: true }).pipe(
|
|
304
|
+
Effect.provide(GerritApiServiceLive),
|
|
305
|
+
Effect.provide(createMockConfigLayer()),
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
const resultPromise = Effect.runPromise(effect)
|
|
309
|
+
|
|
310
|
+
setImmediate(() => {
|
|
311
|
+
// @ts-ignore
|
|
312
|
+
mockChildProcess.stdout.emit('data', Buffer.from(commitMessage))
|
|
313
|
+
mockChildProcess.emit('close', 0)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
await resultPromise
|
|
317
|
+
|
|
318
|
+
const output = capturedStdout.join('')
|
|
319
|
+
expect(output).toContain('<?xml version="1.0" encoding="UTF-8"?>')
|
|
320
|
+
expect(output).toContain('<show_result>')
|
|
321
|
+
expect(output).toContain('<status>error</status>')
|
|
322
|
+
expect(output).toContain('No Change-ID found in HEAD commit')
|
|
323
|
+
})
|
|
324
|
+
})
|