abapgit-agent 1.17.6 → 1.17.8
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/package.json +1 -1
- package/src/commands/unit.js +42 -24
- package/src/config.js +16 -1
package/package.json
CHANGED
package/src/commands/unit.js
CHANGED
|
@@ -327,7 +327,48 @@ Examples:
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
//
|
|
330
|
+
// Coverage threshold enforcement — per file, runs BEFORE JUnit output
|
|
331
|
+
// so the injected failure testcase lands in the correct class's testsuite.
|
|
332
|
+
if (coverage && coverageThreshold > 0) {
|
|
333
|
+
let anyData = false;
|
|
334
|
+
for (const result of results) {
|
|
335
|
+
const stats = result.COVERAGE_STATS || result.coverage_stats;
|
|
336
|
+
if (!stats) continue;
|
|
337
|
+
anyData = true;
|
|
338
|
+
const totalLines = stats.TOTAL_LINES || stats.total_lines || 0;
|
|
339
|
+
const coveredLines = stats.COVERED_LINES || stats.covered_lines || 0;
|
|
340
|
+
if (totalLines === 0) continue;
|
|
341
|
+
const rate = Math.round((coveredLines / totalLines) * 100);
|
|
342
|
+
const className = result._className || 'UNKNOWN';
|
|
343
|
+
if (rate < coverageThreshold) {
|
|
344
|
+
const msg = `${className}: coverage ${rate}% is below threshold ${coverageThreshold}%`;
|
|
345
|
+
if (coverageMode === 'warn') {
|
|
346
|
+
if (!jsonOutput) console.warn(`⚠️ ${msg}`);
|
|
347
|
+
} else {
|
|
348
|
+
if (!jsonOutput) console.error(`❌ ${msg}`);
|
|
349
|
+
hasErrors = true;
|
|
350
|
+
// Inject failure into this class's own testsuite so Jenkins shows
|
|
351
|
+
// which class failed the gate and what its actual coverage was.
|
|
352
|
+
const errors = result.ERRORS || result.errors || [];
|
|
353
|
+
errors.push({
|
|
354
|
+
CLASS_NAME: className,
|
|
355
|
+
METHOD_NAME: 'coverage_threshold',
|
|
356
|
+
ERROR_KIND: 'FAILURE',
|
|
357
|
+
ERROR_TEXT: msg
|
|
358
|
+
});
|
|
359
|
+
result.ERRORS = errors;
|
|
360
|
+
result.FAILED_COUNT = (result.FAILED_COUNT || result.failed_count || 0) + 1;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (!jsonOutput) console.log(`✅ ${className}: coverage ${rate}% meets threshold ${coverageThreshold}%`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!anyData) {
|
|
367
|
+
if (!jsonOutput) console.warn('⚠️ Coverage data unavailable — threshold not enforced');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// JUnit output mode — write XML after threshold check so synthetic failure is included
|
|
331
372
|
if (junitOutput) {
|
|
332
373
|
const xml = buildUnitJUnit(results);
|
|
333
374
|
const outputPath = pathModule.isAbsolute(junitOutput)
|
|
@@ -343,29 +384,6 @@ Examples:
|
|
|
343
384
|
}
|
|
344
385
|
}
|
|
345
386
|
|
|
346
|
-
// Coverage threshold enforcement — aggregated across all files
|
|
347
|
-
if (coverage && coverageThreshold > 0) {
|
|
348
|
-
const totalLines = results.reduce((s, r) => s + ((r.COVERAGE_STATS || r.coverage_stats)?.TOTAL_LINES || (r.COVERAGE_STATS || r.coverage_stats)?.total_lines || 0), 0);
|
|
349
|
-
const coveredLines = results.reduce((s, r) => s + ((r.COVERAGE_STATS || r.coverage_stats)?.COVERED_LINES || (r.COVERAGE_STATS || r.coverage_stats)?.covered_lines || 0), 0);
|
|
350
|
-
|
|
351
|
-
if (totalLines === 0) {
|
|
352
|
-
if (!jsonOutput) console.warn('⚠️ Coverage data unavailable — threshold not enforced');
|
|
353
|
-
} else {
|
|
354
|
-
const rate = Math.round((coveredLines / totalLines) * 100);
|
|
355
|
-
if (rate < coverageThreshold) {
|
|
356
|
-
const msg = `Coverage ${rate}% is below threshold ${coverageThreshold}%`;
|
|
357
|
-
if (coverageMode === 'warn') {
|
|
358
|
-
if (!jsonOutput) console.warn(`⚠️ ${msg}`);
|
|
359
|
-
} else {
|
|
360
|
-
if (!jsonOutput) console.error(`❌ ${msg}`);
|
|
361
|
-
hasErrors = true;
|
|
362
|
-
}
|
|
363
|
-
} else {
|
|
364
|
-
if (!jsonOutput) console.log(`✅ Coverage ${rate}% meets threshold ${coverageThreshold}%`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
387
|
// JSON output mode
|
|
370
388
|
if (jsonOutput) {
|
|
371
389
|
console.log(JSON.stringify(results, null, 2));
|
package/src/config.js
CHANGED
|
@@ -233,6 +233,20 @@ function getScratchWorkspace() {
|
|
|
233
233
|
};
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Get coverage policy from project-level config (.abapgit-agent.json)
|
|
238
|
+
* @returns {{ threshold: number, mode: string, excludes: string[] }}
|
|
239
|
+
*/
|
|
240
|
+
function getCoverageConfig() {
|
|
241
|
+
const projectConfig = loadProjectConfig();
|
|
242
|
+
const coverage = projectConfig?.coverage || {};
|
|
243
|
+
return {
|
|
244
|
+
threshold: coverage.threshold || 0,
|
|
245
|
+
mode: coverage.mode || 'fail',
|
|
246
|
+
excludes: Array.isArray(coverage.excludes) ? coverage.excludes : []
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
236
250
|
module.exports = {
|
|
237
251
|
loadConfig,
|
|
238
252
|
getAbapConfig,
|
|
@@ -246,5 +260,6 @@ module.exports = {
|
|
|
246
260
|
loadProjectConfig,
|
|
247
261
|
getTransportHookConfig,
|
|
248
262
|
getTransportSettings,
|
|
249
|
-
getScratchWorkspace
|
|
263
|
+
getScratchWorkspace,
|
|
264
|
+
getCoverageConfig
|
|
250
265
|
};
|