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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.17.6",
3
+ "version": "1.17.8",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
5
  "files": [
6
6
  "bin/",
@@ -327,7 +327,48 @@ Examples:
327
327
  }
328
328
  }
329
329
 
330
- // JUnit output modewrite XML, then continue to normal output
330
+ // Coverage threshold enforcementper 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
  };