Skip to content

Commit 853b71f

Browse files
DGouronclaude
andcommitted
fix(config): allow empty usernames for single-platform users
GitHub-only users (npm package) couldn't start the server because validateAndEnrichConfig rejected empty gitlabUsername strings. Remove the truthiness check — typeof alone ensures the field is a string while allowing "" for unconfigured platforms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3c8aefa commit 853b71f

File tree

3 files changed

+129
-5
lines changed

3 files changed

+129
-5
lines changed

src/frameworks/config/configLoader.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function enrichRepository(input: RepositoryInput): RepositoryConfig | null {
115115
};
116116
}
117117

118-
function validateAndEnrichConfig(data: unknown): Config {
118+
export function validateAndEnrichConfig(data: unknown): Config {
119119
if (!data || typeof data !== 'object') {
120120
throw new Error('Configuration invalide : objet attendu');
121121
}
@@ -136,11 +136,11 @@ function validateAndEnrichConfig(data: unknown): Config {
136136
throw new Error('Configuration invalide : section "user" manquante');
137137
}
138138
const user = config.user as Record<string, unknown>;
139-
if (typeof user.gitlabUsername !== 'string' || !user.gitlabUsername) {
140-
throw new Error('Configuration invalide : gitlabUsername manquant');
139+
if (typeof user.gitlabUsername !== 'string') {
140+
throw new Error('Invalid configuration: gitlabUsername must be a string');
141141
}
142-
if (typeof user.githubUsername !== 'string' || !user.githubUsername) {
143-
throw new Error('Configuration invalide : githubUsername manquant');
142+
if (typeof user.githubUsername !== 'string') {
143+
throw new Error('Invalid configuration: githubUsername must be a string');
144144
}
145145

146146
// Validate queue
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { validateAndEnrichConfig } from '../../../../frameworks/config/configLoader.js'
2+
3+
function createValidConfig(userOverrides: Record<string, unknown> = {}) {
4+
return {
5+
server: { port: 3000 },
6+
user: {
7+
gitlabUsername: 'my-gitlab-user',
8+
githubUsername: 'my-github-user',
9+
...userOverrides,
10+
},
11+
queue: { maxConcurrent: 2, deduplicationWindowMs: 5000 },
12+
repositories: [],
13+
}
14+
}
15+
16+
describe('validateAndEnrichConfig', () => {
17+
describe('username validation', () => {
18+
it('should accept both non-empty usernames', () => {
19+
const config = createValidConfig()
20+
21+
const result = validateAndEnrichConfig(config)
22+
23+
expect(result.user.gitlabUsername).toBe('my-gitlab-user')
24+
expect(result.user.githubUsername).toBe('my-github-user')
25+
})
26+
27+
it('should accept empty gitlabUsername with non-empty githubUsername', () => {
28+
const config = createValidConfig({
29+
gitlabUsername: '',
30+
githubUsername: 'my-github-user',
31+
})
32+
33+
const result = validateAndEnrichConfig(config)
34+
35+
expect(result.user.gitlabUsername).toBe('')
36+
expect(result.user.githubUsername).toBe('my-github-user')
37+
})
38+
39+
it('should accept empty githubUsername with non-empty gitlabUsername', () => {
40+
const config = createValidConfig({
41+
gitlabUsername: 'my-gitlab-user',
42+
githubUsername: '',
43+
})
44+
45+
const result = validateAndEnrichConfig(config)
46+
47+
expect(result.user.gitlabUsername).toBe('my-gitlab-user')
48+
expect(result.user.githubUsername).toBe('')
49+
})
50+
51+
it('should accept both usernames empty', () => {
52+
const config = createValidConfig({
53+
gitlabUsername: '',
54+
githubUsername: '',
55+
})
56+
57+
const result = validateAndEnrichConfig(config)
58+
59+
expect(result.user.gitlabUsername).toBe('')
60+
expect(result.user.githubUsername).toBe('')
61+
})
62+
63+
it('should reject non-string gitlabUsername', () => {
64+
const config = createValidConfig({ gitlabUsername: 123 })
65+
66+
expect(() => validateAndEnrichConfig(config)).toThrow(
67+
'gitlabUsername',
68+
)
69+
})
70+
71+
it('should reject missing gitlabUsername field', () => {
72+
const config = createValidConfig()
73+
;(config.user as Record<string, unknown>).gitlabUsername = undefined
74+
75+
expect(() => validateAndEnrichConfig(config)).toThrow(
76+
'gitlabUsername',
77+
)
78+
})
79+
80+
it('should reject non-string githubUsername', () => {
81+
const config = createValidConfig({ githubUsername: true })
82+
83+
expect(() => validateAndEnrichConfig(config)).toThrow(
84+
'githubUsername',
85+
)
86+
})
87+
88+
it('should reject missing githubUsername field', () => {
89+
const config = createValidConfig()
90+
;(config.user as Record<string, unknown>).githubUsername = undefined
91+
92+
expect(() => validateAndEnrichConfig(config)).toThrow(
93+
'githubUsername',
94+
)
95+
})
96+
})
97+
})

src/tests/units/interface-adapters/controllers/webhook/eventFilter.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ vi.mock('../../../../../config/loader.js', () => ({
1111
})),
1212
}))
1313

14+
import { loadConfig } from '../../../../../config/loader.js'
1415
import {
1516
filterGitLabEvent,
1617
filterGitLabMrUpdate,
@@ -485,6 +486,32 @@ describe('filterGitHubLabelEvent', () => {
485486
})
486487
})
487488

489+
describe('filterGitLabEvent with empty gitlabUsername', () => {
490+
it('should not process when gitlabUsername is empty', () => {
491+
vi.mocked(loadConfig).mockReturnValueOnce({
492+
user: { gitlabUsername: '', githubUsername: 'claude-reviewer' },
493+
} as ReturnType<typeof loadConfig>)
494+
const event = GitLabEventFactory.createWithReviewerAdded('claude-reviewer')
495+
496+
const result = filterGitLabEvent(event)
497+
498+
expect(result.shouldProcess).toBe(false)
499+
})
500+
})
501+
502+
describe('filterGitHubEvent with empty githubUsername', () => {
503+
it('should not process when githubUsername is empty', () => {
504+
vi.mocked(loadConfig).mockReturnValueOnce({
505+
user: { gitlabUsername: 'claude-reviewer', githubUsername: '' },
506+
} as ReturnType<typeof loadConfig>)
507+
const event = GitHubEventFactory.createReviewRequestedPr('claude-reviewer')
508+
509+
const result = filterGitHubEvent(event)
510+
511+
expect(result.shouldProcess).toBe(false)
512+
})
513+
})
514+
488515
describe('FilterResult type narrowing', () => {
489516
it('should have all required fields when shouldProcess is true', () => {
490517
const event = GitHubEventFactory.createReviewRequestedPr('claude-reviewer')

0 commit comments

Comments
 (0)