Skip to content

Commit 230339f

Browse files
Strum355claude
andcommitted
fix: parse real Maven XML output and remove invokeCommand mocks
parseMavenModuleList was expecting Java List.toString() format ([module-a, module-b]) but Maven 3.9.x with -DforceStdout produces XML (<strings><string>module-a</string>...</strings>). The empty- modules check also expected 'null' but real Maven returns <modules/>. Replace invokeCommand mocks in workspace tests with real Maven invocations so the parser is exercised against actual tool output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1e07b4f commit 230339f

2 files changed

Lines changed: 29 additions & 81 deletions

File tree

src/providers/java_maven.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -390,20 +390,23 @@ function listMavenModules(dir, mvnBin) {
390390
}
391391

392392
const raw = output.toString().trim()
393-
if (!raw || raw === 'null') {
393+
if (!raw || raw.startsWith('<modules')) {
394394
return []
395395
}
396396
return parseMavenModuleList(raw)
397397
}
398398

399399
/**
400-
* @param {string} raw - Raw stdout from mvn (e.g. `[module-a, module-b]`)
400+
* @param {string} raw - Raw stdout from mvn help:evaluate -DforceStdout
401401
* @returns {string[]}
402402
*/
403403
function parseMavenModuleList(raw) {
404-
const match = raw.match(/^\[(.+)]$/)
405-
if (!match) {
406-
return []
404+
const modules = []
405+
const re = /<string>(.+?)<\/string>/g
406+
let m
407+
while ((m = re.exec(raw)) !== null) {
408+
const val = m[1].trim()
409+
if (val) modules.push(val)
407410
}
408-
return match[1].split(',').map(s => s.trim()).filter(Boolean)
411+
return modules
409412
}

test/providers/workspace.test.js

Lines changed: 20 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ suite('discoverWorkspaceCrates', () => {
189189
})
190190
})
191191

192-
suite('discoverMavenModules', () => {
192+
suite('discoverMavenModules', function () {
193+
this.timeout(60_000)
194+
193195
test('returns empty when no pom.xml at root', async () => {
194196
const result = await discoverMavenModules('test/providers/tst_manifests/npm')
195197
expect(result).to.be.an('array')
@@ -198,15 +200,6 @@ suite('discoverMavenModules', () => {
198200

199201
test('returns root pom only when mvn reports no modules', async () => {
200202
const root = path.resolve('test/providers/tst_manifests/maven/maven_no_modules')
201-
const { discoverMavenModules } = await esmock('../../src/providers/java_maven.js', {
202-
'../../src/tools.js': {
203-
getCustom: () => null,
204-
getCustomPath: () => 'mvn',
205-
getGitRootDir: () => null,
206-
getWrapperPreference: () => false,
207-
invokeCommand: () => Buffer.from('null'),
208-
},
209-
})
210203
const result = await discoverMavenModules(root)
211204
expect(result).to.be.an('array')
212205
expect(result).to.have.lengthOf(1)
@@ -215,24 +208,6 @@ suite('discoverMavenModules', () => {
215208

216209
test('discovers multi-module project', async () => {
217210
const root = path.resolve('test/providers/tst_manifests/maven/maven_multi_module')
218-
const { discoverMavenModules } = await esmock('../../src/providers/java_maven.js', {
219-
'../../src/tools.js': {
220-
getCustom: () => null,
221-
getCustomPath: () => 'mvn',
222-
getGitRootDir: () => null,
223-
getWrapperPreference: () => false,
224-
invokeCommand: (bin, args) => {
225-
const pomArg = args.find((a, i) => args[i - 1] === '-f')
226-
if (pomArg && pomArg.includes('module-a')) {
227-
return Buffer.from('null')
228-
}
229-
if (pomArg && pomArg.includes('module-b')) {
230-
return Buffer.from('null')
231-
}
232-
return Buffer.from('[module-a, module-b]')
233-
},
234-
},
235-
})
236211
const result = await discoverMavenModules(root)
237212
expect(result).to.be.an('array')
238213
expect(result).to.have.lengthOf(3)
@@ -244,24 +219,6 @@ suite('discoverMavenModules', () => {
244219

245220
test('discovers nested aggregator modules recursively', async () => {
246221
const root = path.resolve('test/providers/tst_manifests/maven/maven_nested_aggregator')
247-
const { discoverMavenModules } = await esmock('../../src/providers/java_maven.js', {
248-
'../../src/tools.js': {
249-
getCustom: () => null,
250-
getCustomPath: () => 'mvn',
251-
getGitRootDir: () => null,
252-
getWrapperPreference: () => false,
253-
invokeCommand: (bin, args) => {
254-
const pomArg = args.find((a, i) => args[i - 1] === '-f')
255-
if (pomArg && pomArg.endsWith(path.join('parent', 'child', 'pom.xml'))) {
256-
return Buffer.from('null')
257-
}
258-
if (pomArg && pomArg.endsWith(path.join('parent', 'pom.xml'))) {
259-
return Buffer.from('[child]')
260-
}
261-
return Buffer.from('[parent]')
262-
},
263-
},
264-
})
265222
const result = await discoverMavenModules(root)
266223
expect(result).to.be.an('array')
267224
expect(result).to.have.lengthOf(3)
@@ -270,40 +227,28 @@ suite('discoverMavenModules', () => {
270227
expect(result.some(p => p.includes(path.join('parent', 'child', 'pom.xml')))).to.be.true
271228
})
272229

273-
test('returns root pom when mvn command fails', async () => {
230+
test('returns root pom when mvn is not available', async () => {
274231
const root = path.resolve('test/providers/tst_manifests/maven/maven_multi_module')
275-
const { discoverMavenModules } = await esmock('../../src/providers/java_maven.js', {
276-
'../../src/tools.js': {
277-
getCustom: () => null,
278-
getCustomPath: () => 'mvn',
279-
getGitRootDir: () => null,
280-
getWrapperPreference: () => false,
281-
invokeCommand: () => { throw new Error('mvn not found') },
282-
},
283-
})
284-
const result = await discoverMavenModules(root)
285-
expect(result).to.be.an('array')
286-
expect(result).to.have.lengthOf(1)
287-
expect(result[0]).to.equal(path.join(root, 'pom.xml'))
232+
const saved = process.env.TRUSTIFY_DA_MVN_PATH
233+
try {
234+
process.env.TRUSTIFY_DA_MVN_PATH = '/nonexistent/mvn'
235+
process.env.TRUSTIFY_DA_PREFER_MVNW = 'false'
236+
const result = await discoverMavenModules(root)
237+
expect(result).to.be.an('array')
238+
expect(result).to.have.lengthOf(1)
239+
expect(result[0]).to.equal(path.join(root, 'pom.xml'))
240+
} finally {
241+
if (saved !== undefined) {
242+
process.env.TRUSTIFY_DA_MVN_PATH = saved
243+
} else {
244+
delete process.env.TRUSTIFY_DA_MVN_PATH
245+
}
246+
delete process.env.TRUSTIFY_DA_PREFER_MVNW
247+
}
288248
})
289249

290250
test('excludes paths matching workspaceDiscoveryIgnore', async () => {
291251
const root = path.resolve('test/providers/tst_manifests/maven/maven_multi_module')
292-
const { discoverMavenModules } = await esmock('../../src/providers/java_maven.js', {
293-
'../../src/tools.js': {
294-
getCustom: () => null,
295-
getCustomPath: () => 'mvn',
296-
getGitRootDir: () => null,
297-
getWrapperPreference: () => false,
298-
invokeCommand: (bin, args) => {
299-
const pomArg = args.find((a, i) => args[i - 1] === '-f')
300-
if (pomArg && (pomArg.includes('module-a') || pomArg.includes('module-b'))) {
301-
return Buffer.from('null')
302-
}
303-
return Buffer.from('[module-a, module-b]')
304-
},
305-
},
306-
})
307252
const result = await discoverMavenModules(root, {
308253
workspaceDiscoveryIgnore: ['**/module-b/**'],
309254
})

0 commit comments

Comments
 (0)