From 258cac3b8ed9373dc150ef7f569d8cc40f78da5a Mon Sep 17 00:00:00 2001 From: Chrilleweb Date: Wed, 17 Dec 2025 22:34:11 +0100 Subject: [PATCH] Added warnings to scan stats. --- CHANGELOG.md | 2 +- src/commands/scanUsage.ts | 21 ++++++-- src/config/types.ts | 2 + src/services/codeBaseScanner.ts | 1 + src/ui/scan/printStats.ts | 4 ++ test/e2e/cli.warningsCount.e2e.test.ts | 68 ++++++++++++++++++++++++++ 6 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 test/e2e/cli.warningsCount.e2e.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e6aec..6bd7842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/) and [Semant ### Added -- +- Added warnings count to scan usage stats. ### Changed diff --git a/src/commands/scanUsage.ts b/src/commands/scanUsage.ts index f175740..abc2fe6 100644 --- a/src/commands/scanUsage.ts +++ b/src/commands/scanUsage.ts @@ -50,9 +50,6 @@ export async function scanUsage( const endTime = performance.now(); scanResult.stats.duration = (endTime - startTime) / 1000; // Convert to seconds - // Recalculate stats after filtering - calculateStats(scanResult); - // If user explicitly passed --example flag, but the file doesn't exist: if (printMissingExample(opts)) { return { exitWithError: true }; @@ -128,6 +125,9 @@ export async function scanUsage( } } + // Recalculate stats after filtering + calculateStats(scanResult); + // JSON output if (opts.json) { const jsonOutput = createJsonOutput( @@ -237,10 +237,25 @@ function calculateStats(scanResult: ScanResult): ScanResult { scanResult.used.map((u: EnvUsage) => u.variable), ).size; + const warningsCount = + (scanResult.frameworkWarnings?.length ?? 0) + + (scanResult.exampleWarnings?.length ?? 0) + + (scanResult.t3EnvWarnings?.length ?? 0) + + (scanResult.logged?.length ?? 0) + + (scanResult.uppercaseWarnings?.length ?? 0) + + (scanResult.expireWarnings?.length ?? 0) + + (scanResult.inconsistentNamingWarnings?.length ?? 0) + + (scanResult.secrets?.length ?? 0) + + (scanResult.missing.length ?? 0) + + (scanResult.unused.length ?? 0) + + (scanResult.duplicates?.env?.length ?? 0) + + (scanResult.duplicates?.example?.length ?? 0); + scanResult.stats = { filesScanned: scanResult.stats.filesScanned, totalUsages: scanResult.used.length, uniqueVariables, + warningsCount: warningsCount, duration: scanResult.stats.duration, }; diff --git a/src/config/types.ts b/src/config/types.ts index ed0d8f6..9ae5fdd 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -149,6 +149,7 @@ export interface ScanResult { filesScanned: number; totalUsages: number; uniqueVariables: number; + warningsCount: number; duration: number; }; secrets: SecretFinding[]; @@ -188,6 +189,7 @@ export interface ScanJsonEntry { filesScanned: number; totalUsages: number; uniqueVariables: number; + warningsCount: number; duration: number; }; missing: Array<{ diff --git a/src/services/codeBaseScanner.ts b/src/services/codeBaseScanner.ts index a6845e1..57e4652 100644 --- a/src/services/codeBaseScanner.ts +++ b/src/services/codeBaseScanner.ts @@ -70,6 +70,7 @@ export async function scanCodebase(opts: ScanOptions): Promise { filesScanned, totalUsages: filteredUsages.length, uniqueVariables: uniqueVariables.length, + warningsCount: 0, duration: 0, }, duplicates: { diff --git a/src/ui/scan/printStats.ts b/src/ui/scan/printStats.ts index 639fc8a..5e2c7c2 100644 --- a/src/ui/scan/printStats.ts +++ b/src/ui/scan/printStats.ts @@ -4,6 +4,7 @@ interface ScanStats { filesScanned: number; totalUsages: number; uniqueVariables: number; + warningsCount: number; duration: number; } @@ -25,6 +26,9 @@ export function printStats( console.log( chalk.magenta.dim(` Unique variables: ${stats.uniqueVariables}`), ); + console.log( + chalk.magenta.dim(` Warnings: ${stats.warningsCount}`), + ); console.log( chalk.magenta.dim(` Scan duration: ${stats.duration.toFixed(2)}s`), ); diff --git a/test/e2e/cli.warningsCount.e2e.test.ts b/test/e2e/cli.warningsCount.e2e.test.ts new file mode 100644 index 0000000..2d99844 --- /dev/null +++ b/test/e2e/cli.warningsCount.e2e.test.ts @@ -0,0 +1,68 @@ +import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { makeTmpDir, rmrf } from '../utils/fs-helpers.js'; +import { buildOnce, runCli, cleanupBuild } from '../utils/cli-helpers.js'; + +const tmpDirs: string[] = []; + +beforeAll(() => { + buildOnce(); +}); + +afterAll(() => { + cleanupBuild(); +}); + +afterEach(() => { + while (tmpDirs.length) { + const dir = tmpDirs.pop(); + if (dir) rmrf(dir); + } +}); + +function tmpDir() { + const dir = makeTmpDir(); + tmpDirs.push(dir); + return dir; +} + +describe('cli warnings count', () => { + it('should show correct warnings count in scan statistics', () => { + const cwd = tmpDir(); + + // Code using two env vars + fs.writeFileSync( + path.join(cwd, 'index.js'), + ` +const apiKey = process.env.API_KEY; +const apiUrl = process.env.API_URL; +`, + ); + + // .env.example contains: + // - lowercase key (uppercase warning) + // - unused key (unused warning) + fs.writeFileSync( + path.join(cwd, '.env.example'), + ` +api_key=test +UNUSED_KEY=value +`, + ); + + const res = runCli(cwd, ['--scan-usage']); + + /** + * Expected warnings: + * - 2x missing (API_KEY, API_URL) + * - 1x uppercase warning (api_key) + * - 1x unused warning (UNUSED_KEY) + * + * Total: 4 + */ + expect(res.status).toBe(1); + expect(res.stdout).toContain('📊 Scan Statistics:'); + expect(res.stdout).toContain('Warnings: 5'); + }); +});