From 4e6229a2e604245e37128e5ca549fbecef5caf32 Mon Sep 17 00:00:00 2001 From: Ilona Shishov Date: Tue, 21 Nov 2023 11:25:37 +0200 Subject: [PATCH] test: update unit tests Signed-off-by: Ilona Shishov --- package-lock.json | 170 +++++- package.json | 3 +- src/codeActionHandler.ts | 26 +- src/collector.ts | 15 +- src/componentAnalysis.ts | 2 +- src/config.ts | 2 - src/diagnosticsHandler.ts | 7 +- src/providers/go.mod.ts | 16 +- src/providers/package.json.ts | 16 +- src/providers/pom.xml.ts | 14 +- src/providers/requirements.txt.ts | 11 +- src/server.ts | 2 +- test/aggregators.test.ts | 91 --- test/codeActionHandler.test.ts | 78 +++ test/collector.test.ts | 76 ++- test/consumer.test.ts | 117 ---- test/providers/go.mod.test.ts | 729 ++++++++++------------ test/providers/package.json.test.ts | 240 +++++--- test/providers/pom.xml.test.ts | 782 ++++++++++++------------ test/providers/requirements.txt.test.ts | 114 ++-- test/vulnerability.test.ts | 206 +++---- 21 files changed, 1360 insertions(+), 1357 deletions(-) delete mode 100644 test/aggregators.test.ts create mode 100644 test/codeActionHandler.test.ts delete mode 100644 test/consumer.test.ts diff --git a/package-lock.json b/package-lock.json index 66ed6725..93d13160 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.1-ea.18", "license": "Apache-2.0", "dependencies": { - "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.49", + "@RHEcosystemAppEng/exhort-javascript-api": "^0.1.0", "@xml-tools/ast": "^5.0.5", "@xml-tools/parser": "^1.0.11", "json-to-ast": "^2.1.0", @@ -31,6 +31,7 @@ "fake-exec": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", + "sinon": "^17.0.1", "ts-node": "^10.9.1", "typedoc": "^0.25.3", "typescript": "^5.2.2" @@ -38,7 +39,7 @@ }, "../exhort-javascript-api": { "name": "@RHEcosystemAppEng/exhort-javascript-api", - "version": "0.0.2-ea.49", + "version": "0.0.2-ea.50", "extraneous": true, "license": "Apache-2.0", "dependencies": { @@ -836,9 +837,9 @@ } }, "node_modules/@RHEcosystemAppEng/exhort-javascript-api": { - "version": "0.0.2-ea.49", - "resolved": "https://npm.pkg.github.com/download/@RHEcosystemAppEng/exhort-javascript-api/0.0.2-ea.49/0380891b685a3eb30653010a6849669553ea4bb9", - "integrity": "sha512-APOe3QjMjE+Dx9ASZPN97Tpxq/fTvHic9IBTvfCeWhIK5M/WJ562B6U/YG7qjQmHfUur8jHXZOQpJ/bXfNBKDA==", + "version": "0.1.0", + "resolved": "https://npm.pkg.github.com/download/@RHEcosystemAppEng/exhort-javascript-api/0.1.0/3159c652ef143fbd27ea8decb881d0f460384cdf", + "integrity": "sha512-YstZq1eAUYitnu5DKP3jcfug93B0EAmsK88FAgLQFp5fmFSaCnpnbkyeKpLzulGC4T5bgnq/t1/QswkAWtdoLg==", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.23.2", @@ -855,6 +856,50 @@ "npm": ">= 9.0.0" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -2854,6 +2899,12 @@ "node": ">=0.10.0" } }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3098,6 +3149,12 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3161,6 +3218,12 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3584,6 +3647,46 @@ "url": "https://nearley.js.org/#give-to-nearley" } }, + "node_modules/nise": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -4009,6 +4112,15 @@ "node": ">=8" } }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4415,6 +4527,54 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "devOptional": true }, + "node_modules/sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", diff --git a/package.json b/package.json index 3ee0094d..816a7ac8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "dist" ], "dependencies": { - "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.49", + "@RHEcosystemAppEng/exhort-javascript-api": "^0.1.0", "@xml-tools/ast": "^5.0.5", "@xml-tools/parser": "^1.0.11", "json-to-ast": "^2.1.0", @@ -43,6 +43,7 @@ "fake-exec": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", + "sinon": "^17.0.1", "ts-node": "^10.9.1", "typedoc": "^0.25.3", "typescript": "^5.2.2" diff --git a/src/codeActionHandler.ts b/src/codeActionHandler.ts index cedc55cb..4c7c91b5 100644 --- a/src/codeActionHandler.ts +++ b/src/codeActionHandler.ts @@ -5,42 +5,22 @@ 'use strict'; import { CodeAction, CodeActionKind, Diagnostic } from 'vscode-languageserver/node'; -import { codeActionsMap } from './diagnosticsHandler'; import { globalConfig } from './config'; import { RHDA_DIAGNOSTIC_SOURCE } from './constants'; /** * Retrieves code actions based on diagnostics and file type. * @param diagnostics - An array of available diagnostics. - * @param fileType - The type of the file based on ecosystem (e.g., 'pom.xml'). * @returns An array of CodeAction objects to be made available to the user. */ -function getDiagnosticsCodeActions( diagnostics: Diagnostic[], fileType: string ): CodeAction[] { +function getDiagnosticsCodeActions(diagnostics: Diagnostic[]): CodeAction[] { + const hasRhdaDiagonostic = diagnostics.some(diagnostic => diagnostic.source === RHDA_DIAGNOSTIC_SOURCE); const codeActions: CodeAction[] = []; - let hasRhdaDiagonostic: boolean = false; - - for (const diagnostic of diagnostics) { - const codeAction = codeActionsMap[diagnostic.range.start.line + '|' + diagnostic.range.start.character]; - if (codeAction) { - - if (fileType === 'pom.xml') { - // add Red Hat repository recommendation command to action - codeAction.command = { - title: 'RedHat repository recommendation', - command: globalConfig.triggerRHRepositoryRecommendationNotification, - }; - } - codeActions.push(codeAction); - - } - if (!hasRhdaDiagonostic) { - hasRhdaDiagonostic = diagnostic.source === RHDA_DIAGNOSTIC_SOURCE; - } - } if (globalConfig.triggerFullStackAnalysis && hasRhdaDiagonostic) { codeActions.push(generateFullStackAnalysisAction()); } + return codeActions; } diff --git a/src/collector.ts b/src/collector.ts index 8609b448..b1121ef0 100644 --- a/src/collector.ts +++ b/src/collector.ts @@ -5,6 +5,7 @@ 'use strict'; import { Range } from 'vscode-languageserver'; +import { isDefined } from './utils'; /** * Represents a position inside the manifest file with line and column information. @@ -43,10 +44,11 @@ export interface IDependency { * Represents a dependency and implements the IDependency interface. */ export class Dependency implements IDependency { + public version: IPositionedString + public context: IPositionedContext + constructor( - public name: IPositionedString, - public version: IPositionedString = {} as IPositionedString, - public context: IPositionedContext = {} as IPositionedContext, + public name: IPositionedString ) {} } @@ -56,8 +58,9 @@ export class Dependency implements IDependency { * @returns The range within the text document that represents the dependency. */ export function getRange (dep: IDependency): Range { - const pos: IPosition = dep.version.position; - if (pos.line !== 0) { + + if (isDefined(dep, 'version', 'position')) { + const pos: IPosition = dep.version.position; const length = dep.version.value.length; return { start: { @@ -123,7 +126,7 @@ export class EcosystemDependencyResolver { } /** - * RResolves a dependency reference to its actual name in the specified ecosystem. + * Resolves a dependency reference in a specified ecosystem to its name and version string. * @param ref - The reference string to resolve. * @returns The resolved name of the dependency. */ diff --git a/src/componentAnalysis.ts b/src/componentAnalysis.ts index ae0223b9..b81ae5f0 100644 --- a/src/componentAnalysis.ts +++ b/src/componentAnalysis.ts @@ -86,7 +86,7 @@ class AnalysisResponse implements IAnalysisResponse { sources.forEach(source => { source.dependencies.forEach(d => { if (isDefined(d, 'ref') && isDefined(d, 'issues')) { - const dd = new DependencyData(source.id, d.issues.length, isDefined(d, 'highestVulnerability', 'severity') ? d.highestVulnerability.severity : 'UNKNOWN'); + const dd = new DependencyData(source.id, d.issues.length, isDefined(d, 'highestVulnerability', 'severity') ? d.highestVulnerability.severity : 'NONE'); if (this.dependencies[d.ref] === undefined) { this.dependencies[d.ref] = []; } diff --git a/src/config.ts b/src/config.ts index 3796e709..9617678a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -11,7 +11,6 @@ export class Config { triggerFullStackAnalysis: string; - triggerRHRepositoryRecommendationNotification: string; telemetryId: string; utmSource: string; exhortDevMode: string; @@ -30,7 +29,6 @@ export class Config */ constructor() { this.triggerFullStackAnalysis = process.env.VSCEXT_TRIGGER_FULL_STACK_ANALYSIS || ''; - this.triggerRHRepositoryRecommendationNotification = process.env.VSCEXT_TRIGGER_REDHAT_REPOSITORY_RECOMMENDATION_NOTIFICATION || ''; this.telemetryId = process.env.VSCEXT_TELEMETRY_ID || ''; this.utmSource = process.env.VSCEXT_UTM_SOURCE || ''; this.exhortDevMode = process.env.VSCEXT_EXHORT_DEV_MODE || 'false'; diff --git a/src/diagnosticsHandler.ts b/src/diagnosticsHandler.ts index befc1f5b..cef96b6c 100644 --- a/src/diagnosticsHandler.ts +++ b/src/diagnosticsHandler.ts @@ -130,9 +130,4 @@ async function performDiagnostics(diagnosticFilePath: string, contents: string, diagnosticsPipeline.reportDiagnostics(); } -/** - * Map of code actions. - */ -const codeActionsMap = new Map(); - -export { performDiagnostics, codeActionsMap }; \ No newline at end of file +export { performDiagnostics }; \ No newline at end of file diff --git a/src/providers/go.mod.ts b/src/providers/go.mod.ts index e3ee7a45..83f3fe2c 100644 --- a/src/providers/go.mod.ts +++ b/src/providers/go.mod.ts @@ -73,7 +73,6 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I */ private registerReplacement(line: string, index: number) { const lineData: string[] = line.split('=>'); - if (lineData.length !== 2) { return; } let originalDepData = DependencyProvider.getDependencyData(lineData[0]); const replacementDepData = DependencyProvider.getDependencyData(lineData[1]); @@ -81,11 +80,9 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I if (!originalDepData) { originalDepData = {name: DependencyProvider.clean(lineData[0]), version: null, index: null}; } if (!replacementDepData) { return; } - const replaceDependency = new Dependency( - { value: replacementDepData.name, position: { line: 0, column: 0 } }, - { value: 'v' + replacementDepData.version, position: { line: index + 1, column: (line.lastIndexOf(lineData[1]) + replacementDepData.index) } }, - ); - + const replaceDependency = new Dependency({ value: replacementDepData.name, position: { line: 0, column: 0 } }); + replaceDependency.version = { value: 'v' + replacementDepData.version, position: { line: index + 1, column: (line.lastIndexOf(lineData[1]) + replacementDepData.index) } }; + this.replacementMap.set(originalDepData.name + (originalDepData.version ? ('@v' + originalDepData.version) : ''), replaceDependency); } @@ -109,10 +106,9 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I const depData = DependencyProvider.getDependencyData(line); if (!depData) { return null; } - return new Dependency( - { value: depData.name, position: { line: 0, column: 0 } }, - { value: 'v' + depData.version, position: { line: index + 1, column: depData.index } }, - ); + const dep = new Dependency({ value: depData.name, position: { line: 0, column: 0 } }); + dep.version = { value: 'v' + depData.version, position: { line: index + 1, column: depData.index } }; + return dep; } /** diff --git a/src/providers/package.json.ts b/src/providers/package.json.ts index 56268576..5bbf69eb 100644 --- a/src/providers/package.json.ts +++ b/src/providers/package.json.ts @@ -11,7 +11,7 @@ import { IDependencyProvider, EcosystemDependencyResolver, IDependency, Dependen * Process entries found in the package.json file. */ export class DependencyProvider extends EcosystemDependencyResolver implements IDependencyProvider { - private classes: string[] = ['dependencies']; + classes: string[] = ['dependencies']; constructor() { super('npm'); // set ecosystem to 'npm' @@ -36,10 +36,9 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I .filter(c => this.classes.includes(c.key.value)) .flatMap(c => c.value.children) .map(c => { - return new Dependency( - { value: c.key.value, position: {line: c.key.loc.start.line, column: c.key.loc.start.column + 1} }, - { value: c.value.value, position: {line: c.value.loc.start.line, column: c.value.loc.start.column + 1} }, - ); + const dep = new Dependency({ value: c.key.value, position: {line: c.key.loc.start.line, column: c.key.loc.start.column + 1} }); + dep.version = { value: c.value.value, position: {line: c.value.loc.start.line, column: c.value.loc.start.column + 1} }; + return dep; }); } @@ -49,14 +48,17 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I * @returns A Promise resolving to an array of IDependency objects representing collected dependencies. */ async collect(contents: string): Promise { + let ast: jsonAst; + try { - const ast: jsonAst = this.parseJson(contents); - return this.mapDependencies(ast); + ast = this.parseJson(contents); } catch (err) { if (err instanceof SyntaxError) { return []; } throw err; } + + return this.mapDependencies(ast); } } diff --git a/src/providers/pom.xml.ts b/src/providers/pom.xml.ts index dd63c8b0..b188c319 100644 --- a/src/providers/pom.xml.ts +++ b/src/providers/pom.xml.ts @@ -107,12 +107,6 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I { value: `${d.groupId.textContents[0].text}/${d.artifactId.textContents[0].text}`, position: { line: d.element.position.startLine, column: d.element.position.startColumn } }, ); - - dep.context = { value: '', range: { - start: { line: d.element.position.startLine - 1, character: d.element.position.startColumn - 1 }, - end: { line: d.element.position.endLine - 1, character: d.element.position.endColumn } - }, - }; if (d.version && d.version.textContents.length > 0) { const versionVal = d.version.textContents[0]; @@ -121,11 +115,11 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I position: { line: versionVal.position.startLine, column: versionVal.position.startColumn }, }; } else { - dep.version = { - value: '', - position: { line: 0, column: 0 }, + dep.context = { value: dependencyTemplate(d.element), range: { + start: { line: d.element.position.startLine - 1, character: d.element.position.startColumn - 1 }, + end: { line: d.element.position.endLine - 1, character: d.element.position.endColumn } + }, }; - dep.context.value = dependencyTemplate(d.element); } return dep; diff --git a/src/providers/requirements.txt.ts b/src/providers/requirements.txt.ts index 9b759226..810a645a 100644 --- a/src/providers/requirements.txt.ts +++ b/src/providers/requirements.txt.ts @@ -31,8 +31,8 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I * @returns An IDependency object representing the parsed dependency or null if no dependency is found. */ private parseLine(line: string, index: number): IDependency | null { - line = line.split('#')[0].trim(); // Remove comments - if (!line) { return null; } // Skip empty lines + line = line.split('#')[0]; // Remove comments + if (!line.trim()) { return null; } // Skip empty lines const lineData: string[] = line.split(/[==,>=,<=]+/); if (lineData.length !== 2) { return null; } // Skip invalid lines @@ -40,10 +40,9 @@ export class DependencyProvider extends EcosystemDependencyResolver implements I const depName: string = lineData[0].trim().toLowerCase(); const depVersion: string = lineData[1].trim(); - return new Dependency( - { value: depName, position: { line: 0, column: 0 } }, - { value: depVersion, position: { line: index + 1, column: line.indexOf(depVersion) + 1 } }, - ); + const dep = new Dependency ({ value: depName, position: { line: 0, column: 0 } }); + dep.version = { value: depVersion, position: { line: index + 1, column: line.indexOf(depVersion) + 1 } }; + return dep; } /** diff --git a/src/server.ts b/src/server.ts index 32139b9c..fe99e1c4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -109,7 +109,7 @@ connection.onDidChangeConfiguration(() => { * Handles code action requests from client. */ connection.onCodeAction((params): CodeAction[] => { - return getDiagnosticsCodeActions(params.context.diagnostics, path.basename(params.textDocument.uri)); + return getDiagnosticsCodeActions(params.context.diagnostics); }); connection.listen(); diff --git a/test/aggregators.test.ts b/test/aggregators.test.ts deleted file mode 100644 index 466deb55..00000000 --- a/test/aggregators.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { expect } from 'chai'; -import { Range } from 'vscode-languageserver'; -import { DependencyProvider as PackageJson } from '../src/providers/package.json'; -import { DependencyProvider as PomXml } from '../src/providers/pom.xml'; -import { NoopVulnerabilityAggregator, MavenVulnerabilityAggregator } from '../src/aggregators'; -import { Vulnerability, ANALYTICS_SOURCE } from '../src/vulnerability'; - -const dummyRange: Range = { - start: { - line: 3, - character: 4 - }, - end: { - line: 3, - character: 10 - } -} - -describe('Noop vulnerability aggregator tests', () => { - - it('Test Noop aggregator', async () => { - let noopVulnerabilityAggregator = new NoopVulnerabilityAggregator(new PackageJson()); - // let vulnerability = new Vulnerability(dummyRange, 0, 'pkg:npm/MockPkg@1.2.3', null, '', '', null, ''); - let vulnerability = new Vulnerability(dummyRange, 'pkg:npm/MockPkg@1.2.3', 0, ''); - let aggVulnerability = noopVulnerabilityAggregator.aggregate(vulnerability); - - // const msg = 'pkg:npm/MockPkg@1.2.3\nRecommendation: No RedHat packages to recommend'; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Information, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - expect(noopVulnerabilityAggregator.isNewVulnerability).to.equal(true); - expect(aggVulnerability.provider.ecosystem).is.eql('npm') - // expect(aggVulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - }); -}); - -describe('Maven vulnerability aggregator tests', () => { - - it('Test Maven aggregator with one vulnerability', async () => { - let mavenVulnerabilityAggregator = new MavenVulnerabilityAggregator(new PomXml()); - // let vulnerability = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, null, '', '', null, ''); - let vulnerability = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, ''); - let aggVulnerability = mavenVulnerabilityAggregator.aggregate(vulnerability); - - // const msg = 'pkg:maven/MockPkg@1.2.3\nRecommendation: No RedHat packages to recommend'; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Information, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - expect(mavenVulnerabilityAggregator.isNewVulnerability).to.equal(true); - expect(aggVulnerability.provider.ecosystem).is.eql('maven') - // expect(aggVulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - }); - - it('Test Maven aggregator with two identical vulnerability', async () => { - let mavenVulnerabilityAggregator = new MavenVulnerabilityAggregator(new PomXml()); - // let vulnerability1 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, null, '', '', null, ''); - let vulnerability1 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, ''); - let aggVulnerability1 = mavenVulnerabilityAggregator.aggregate(vulnerability1); - // let vulnerability2 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, null, '', '', null, ''); - let vulnerability2 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg@1.2.3', 0, ''); - let aggVulnerability2 = mavenVulnerabilityAggregator.aggregate(vulnerability2); - - expect(mavenVulnerabilityAggregator.isNewVulnerability).to.equal(false); - expect(mavenVulnerabilityAggregator.vulnerabilities.size).to.equal(1); - expect(aggVulnerability1.provider.ecosystem).is.eql('maven') - expect(aggVulnerability2.provider.ecosystem).is.eql('maven') - }); - - it('Test Maven aggregator with two different vulnerability', async () => { - let mavenVulnerabilityAggregator = new MavenVulnerabilityAggregator(new PomXml()); - // let vulnerability1 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg1@1.2.3', 0, null, '', '', null, ''); - let vulnerability1 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg1@1.2.3', 0, ''); - let aggVulnerability1 = mavenVulnerabilityAggregator.aggregate(vulnerability1); - // let vulnerability2 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg2@1.2.3', 0, null, '', '', null, ''); - let vulnerability2 = new Vulnerability(dummyRange, 'pkg:maven/MockPkg2@1.2.3', 0, ''); - let aggVulnerability2 = mavenVulnerabilityAggregator.aggregate(vulnerability2); - - expect(mavenVulnerabilityAggregator.isNewVulnerability).to.equal(true); - expect(mavenVulnerabilityAggregator.vulnerabilities.size).to.equal(2); - expect(aggVulnerability1.provider.ecosystem).is.eql('maven') - expect(aggVulnerability2.provider.ecosystem).is.eql('maven') - }); -}); \ No newline at end of file diff --git a/test/codeActionHandler.test.ts b/test/codeActionHandler.test.ts new file mode 100644 index 00000000..3267d378 --- /dev/null +++ b/test/codeActionHandler.test.ts @@ -0,0 +1,78 @@ +'use strict'; + +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { Range } from 'vscode-languageserver'; +import { CodeAction, CodeActionKind, Diagnostic } from 'vscode-languageserver/node'; +import * as config from '../src/config'; +import { RHDA_DIAGNOSTIC_SOURCE } from '../src/constants'; +import { getDiagnosticsCodeActions } from '../src/codeActionHandler'; + +describe('Code Action Handler test', () => { + + const mockRange: Range = { + start: { + line: 123, + character: 123 + }, + end: { + line: 456, + character: 456 + } + }; + + const mockDiagnostics: Diagnostic[] = [ + { + severity: 1, + range: mockRange, + message: 'mock message', + source: RHDA_DIAGNOSTIC_SOURCE, + }, + { + severity: 2, + range: mockRange, + message: 'another mock message', + source: RHDA_DIAGNOSTIC_SOURCE, + } + ]; + + it('should return an empty array if no RHDA diagnostics are present', () => { + const diagnostics: Diagnostic[] = []; + + const codeActions = getDiagnosticsCodeActions(diagnostics); + + expect(codeActions).to.be.an('array').that.is.empty; + }); + + it('should generate code actions for RHDA diagnostics when full stack analysis action is provided', async () => { + let globalConfig = { + triggerFullStackAnalysis: 'mockAction' + }; + sinon.stub(config, 'globalConfig').value(globalConfig); + + const codeActions: CodeAction[] = getDiagnosticsCodeActions(mockDiagnostics); + + expect(codeActions).to.deep.equal( + [ + { + title: 'Detailed Vulnerability Report', + kind: CodeActionKind.QuickFix, + command: { + title: 'Analytics Report', + command: 'mockAction' } + } + ] + ); + }); + + it('should return an empty array when no full stack analysis action is provided', async () => { + let globalConfig = { + triggerFullStackAnalysis: '' + }; + sinon.stub(config, 'globalConfig').value(globalConfig); + + const codeActions: CodeAction[] = getDiagnosticsCodeActions(mockDiagnostics); + + expect(codeActions).to.be.an('array').that.is.empty; + }); +}); \ No newline at end of file diff --git a/test/collector.test.ts b/test/collector.test.ts index ddc0cb7d..93abb9c4 100644 --- a/test/collector.test.ts +++ b/test/collector.test.ts @@ -1,31 +1,59 @@ 'use strict'; import { expect } from 'chai'; -import { Dependency, IPosition, ValueType, Variant, KeyValueEntry, DependencyMap } from '../src/collector'; +import { Dependency, DependencyMap, getRange } from '../src/collector'; +import { DependencyProvider } from '../src/providers/pom.xml'; describe('Collector util test', () => { - const pos: IPosition = { - line: 123, - column: 123 - }; - - // define manifest dependencies - const reqDeps: Array = [ - new Dependency(new KeyValueEntry('mockGtoup1/mockArtifact1', pos, new Variant(ValueType.String, 'mockVersion1'), pos)), - new Dependency(new KeyValueEntry('mockGtoup2/mockArtifact2', pos, new Variant(ValueType.String, 'mockVersion2'), pos)) - ]; - - // define api response dependencies - const resDeps: any[] = [ - { ref: 'pkg:maven/mockGtoup1/mockArtifact1@mockVersion1' }, - { ref: 'pkg:maven/mockGtoup2/mockArtifact2@mockVersion2' } - ]; - - it('create map for maven dependecies', async () => { - const ecosystem = 'maven'; - const map = new DependencyMap(reqDeps); - expect(map.get(resDeps[0].ref.split('@')[0].replace(`pkg:${ecosystem}/`, ''))).to.eql(reqDeps[0]); - expect(map.get(resDeps[1].ref.split('@')[0].replace(`pkg:${ecosystem}/`, ''))).to.eql(reqDeps[1]); - }); + // Mock manifest dependency collection + const reqDeps: Dependency[] = [ + new Dependency ({ value: 'mockGroupId1/mockArtifact1', position: { line: 0, column: 0 } }), + new Dependency ({ value: 'mockGroupId2/mockArtifact2', position: { line: 0, column: 0 } }) + ]; + + it('should create map of dependecies', async () => { + + const depMap = new DependencyMap(reqDeps); + + expect(Object.fromEntries(depMap.mapper)).to.eql({ + 'mockGroupId1/mockArtifact1': reqDeps[0], + 'mockGroupId2/mockArtifact2': reqDeps[1] + }); + }); + + it('should get dependency from dependency map', async () => { + + const depMap = new DependencyMap(reqDeps); + + expect(depMap.get(reqDeps[0].name.value)).to.eq(reqDeps[0]); + expect(depMap.get(reqDeps[1].name.value)).to.eq(reqDeps[1]); + }); + + it('should return dependency range', async () => { + + reqDeps[0].version = { value: 'mockVersion', position: { line: 123, column: 123 } }; + reqDeps[1].context = { value: 'mockRange', range: { + start: { line: 123, character: 123 }, + end: { line: 456, character: 456 } + }, + }; + + expect(getRange(reqDeps[0])).to.eql( + { + start: { line: 122, character: 122 }, + end: { line: 122, character: 133 } + } + ); + + expect(getRange(reqDeps[1])).to.eql(reqDeps[1].context.range); + }); + + it('should resolves a dependency reference in a specified ecosystem to its name and version string', async () => { + const mavenDependencyProvider = new DependencyProvider(); + + const resolvedRef = mavenDependencyProvider.resolveDependencyFromReference('pkg:maven/mockGroupId1/mockArtifact1@mockVersion1'); + + expect(resolvedRef).to.eq('mockGroupId1/mockArtifact1@mockVersion1'); + }); }) diff --git a/test/consumer.test.ts b/test/consumer.test.ts deleted file mode 100644 index 93162992..00000000 --- a/test/consumer.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { expect } from 'chai'; -import { DependencyProvider as PackageJson } from '../src/providers/package.json'; -import { SecurityEngine, DiagnosticsPipeline } from '../src/consumers'; -import { NoopVulnerabilityAggregator } from '../src/aggregators'; -import { Diagnostic } from 'vscode-languageserver'; - -const config = {}; -const diagnosticFilePath = "path/to/mock/diagnostic"; -const pkg = { - name: { - value: "MockPkg", - position: { - line: 20, - column: 6 - } - }, - version: { - value: "1.2.3", - position: { - line: 20, - column: 17 - } - }, - context: { - value: "MockPkg1.2.3", - range: { - start: { - line: 20, - character: 6 - }, - end: { - line: 20, - character:17 - } - } - } -}; - -describe('Response consumer test', () => { - it('Consume response without vulnerabilities', () => { - let diagnostics: Diagnostic[] = []; - let packageAggregator = new NoopVulnerabilityAggregator(new PackageJson()); - const dependency = { - ref: "pkg:npm/MockPkg@1.2.3", - issues: [ - ], - transitive: [ - ], - recommendation: { - name: 'mockRecommendationName', - version: 'mockRecommendationVersion', - }, - remediations: { - }, - highestVulnerability: { - }, - } - - let pipeline = new DiagnosticsPipeline(SecurityEngine, pkg, config, diagnostics, packageAggregator, diagnosticFilePath); - pipeline.run(dependency); - const secEng = pipeline.item as SecurityEngine; - // const msg = 'MockPkg@1.2.3\nRecommendation: mockRecommendationName:mockRecommendationVersion'; - - // expect(diagnostics.length).equal(1); - expect(diagnostics.length).equal(0); - expect(secEng.issuesCount).equal(0); - // expect(diagnostics[0].message.toString().replace(/\s/g, "")).equal(msg.toString().replace(/\s/g, "")); - }); - - it('Consume response with vulnerabilities', () => { - let diagnostics: Diagnostic[] = []; - let packageAggregator = new NoopVulnerabilityAggregator(new PackageJson()); - const dependency = { - ref: "pkg:npm/MockPkg@1.2.3", - issues: [ - { - id: "MockIssue", - }, - ], - transitive: [ - ], - recommendation: null, - remediations: { - "mockCVE": { - npmPackage: "pkg:npm/MockPkg@4.5.6", - }, - }, - highestVulnerability: { - id: "MockIssue", - severity: "MockSeverity", - }, - } - - let pipeline = new DiagnosticsPipeline(SecurityEngine, pkg, config, diagnostics, packageAggregator, diagnosticFilePath); - pipeline.run(dependency); - const secEng = pipeline.item as SecurityEngine; - // const msg = "MockPkg@1.2.3\nKnown security vulnerabilities: 1\nHighest severity: MockSeverity\nHas remediation: Yes"; - const msg = "MockPkg@1.2.3\nKnown security vulnerabilities: 1\nHighest severity: MockSeverity"; - - expect(diagnostics.length).equal(1); - expect(secEng.issuesCount).equal(1); - expect(diagnostics[0].message.toString().replace(/\s/g, "")).equal(msg.toString().replace(/\s/g, "")); - }); - - it('Consume invalid response', () => { - let diagnostics: Diagnostic[] = []; - let packageAggregator = new NoopVulnerabilityAggregator(new PackageJson()); - const dependency = { - ref: "pkg:npm/MockPkg@1.2.3", - }; - - let pipeline = new DiagnosticsPipeline(SecurityEngine, pkg, config, diagnostics, packageAggregator, diagnosticFilePath); - pipeline.run(dependency); - - expect(diagnostics.length).equal(0); - }); -}); diff --git a/test/providers/go.mod.test.ts b/test/providers/go.mod.test.ts index 7a4fe02a..a5ab450d 100644 --- a/test/providers/go.mod.test.ts +++ b/test/providers/go.mod.test.ts @@ -1,434 +1,347 @@ +'use strict'; + import { expect } from 'chai'; import { DependencyProvider } from '../../src/providers/go.mod'; -const fake = require('fake-exec'); - -describe('Golang go.mod parser test', () => { - const provider = new DependencyProvider(); - - it('tests valid go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - require ( - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 - github.com/stretchr/testify v1.2.2 - ) - go 1.13 - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 4, column: 41 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.1', position: { line: 5, column: 40 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.0.0', position: { line: 6, column: 43 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.2', position: { line: 7, column: 41 } } - }); - }); - - it('tests go.mod with comments', async () => { - const deps = await provider.collect(`// This is start point. - module github.com/alecthomas/kingpin - require ( - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // Valid data before this. - // Extra comment in require section. - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 - ) - go 1.13 - // Final notes. - `); - expect(deps.length).equal(3); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 4, column: 41 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.0.0', position: { line: 6, column: 43 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.2', position: { line: 7, column: 41 } } - }); - }); - - it('tests empty go.mod', async () => { - const deps = await provider.collect(``); - expect(deps).is.eql([]); - }); - - it('tests empty lines in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - - require ( +describe('Golang Gomudules go.mod parser test', () => { + let dependencyProvider: DependencyProvider; - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // Valid data before this. - - github.com/stretchr/testify v1.2.2 - - ) - go 1.13 - - `); - expect(deps.length).equal(2); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 6, column: 41 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.2', position: { line: 8, column: 41 } } - }); - }); - - it('tests deps with spaces before and after comparators', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - require ( - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 - ) - go 1.13 - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 4, column: 44 } } + beforeEach(() => { + dependencyProvider = new DependencyProvider(); }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.1', position: { line: 5, column: 49 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.0.0', position: { line: 6, column: 51 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.2', position: { line: 7, column: 45 } } - }); - }); - - it('tests alpha beta and extra for version in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - - require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible - github.com/davecgh/go-spew v1.1.1+incompatible - github.com/pmezard/go-difflib v1.3.0+version - github.com/stretchr/testify v1.2.2+incompatible-version - github.com/regen-network/protobuf v1.3.2-alpha.regen.4 - github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 - github.com/btcsuite/btcd v0.20.1-beta - ) - go 1.13 - `); - expect(deps.length).equal(8); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.1.3-alpha', position: { line: 5, column: 39 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v2.5.2-alpha+incompatible', position: { line: 6, column: 34 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.1+incompatible', position: { line: 7, column: 38 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.0+version', position: { line: 8, column: 41 } } - }); - expect(deps[4]).is.eql({ - name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.2+incompatible-version', position: { line: 9, column: 39 } } + it('tests empty go.mod', async () => { + const deps = await dependencyProvider.collect(``); + expect(deps).is.eql([]); }); - expect(deps[5]).is.eql({ - name: { value: 'github.com/regen-network/protobuf', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.2-alpha.regen.4', position: { line: 10, column: 45 } } - }); - expect(deps[6]).is.eql({ - name: { value: 'github.com/vmihailenco/msgpack/v5', position: { line: 0, column: 0 } }, - version: { value: 'v5.0.0-beta.1', position: { line: 11, column: 45 } } - }); - expect(deps[7]).is.eql({ - name: { value: 'github.com/btcsuite/btcd', position: { line: 0, column: 0 } }, - version: { value: 'v0.20.1-beta', position: { line: 12, column: 36 } } - }); - }); - it('tests replace statements in go.mod', async () => { - const deps = await provider.collect(` + it('tests require statement in go.mod', async () => { + const deps = await dependencyProvider.collect(` module github.com/alecthomas/kingpin - go 1.13 - require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible - github.com/davecgh/go-spew v1.1.1+incompatible - github.com/pmezard/go-difflib v1.3.0 - github.com/stretchr/testify v1.2.2+incompatible-version - github.com/regen-network/protobuf v1.3.2-alpha.regen.4 - github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 - github.com/btcsuite/btcd v0.0.0-20151022065526-2efee857e7cf - ) - replace ( - github.com/alecthomas/units v0.1.3-alpha => github.com/test-user/units v13.3.2 // Required by OLM - github.com/alecthomas/units v0.1.3 => github.com/test-user/units v13.3.2 // Required by OLM - github.com/pierrec/lz4 => github.com/pierrec/lz4 v3.4.2 // Required by prometheus-operator - github.com/pierrec/lz4 v3.4.1 => github.com/pierrec/lz4 v3.4.3 // same-module with diff version in replace - github.com/davecgh/go-spew v1.1.1+incompatible => github.com/davecgh/go-spew v1.1.2 - github.com/stretchr/testify => github.com/stretchr-1/testify v1.2.3 // test with module and with one import package - github.com/regen-network/protobuf => github.com/regen-network/protobuf1 v1.3.2 // test with module and with one import package - github.com/pmezard/go-difflib v1.3.0 => github.com/pmezard/go-difflib v0.0.0-20151022065526-2efee857e7cf // semver to pseudo - github.com/btcsuite/btcd v0.0.0-20151022065526-2efee857e7cf => github.com/btcsuite/btcd v0.20.1-beta // pseudo to semver - github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 => ./msgpack/v5 // replace with local module - ) - `); - expect(deps.length).equal(8); - expect(deps[0]).is.eql({ - name: { value: 'github.com/test-user/units', position: { line: 0, column: 0 } }, - version: { value: 'v13.3.2', position: { line: 16, column: 82 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v3.4.2', position: { line: 18, column: 60 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.2', position: { line: 20, column: 88 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 23, column: 81 } } - }); - expect(deps[4]).is.eql({ - name: { value: 'github.com/stretchr-1/testify', position: { line: 0, column: 0 } }, - version: { value: 'v1.2.3', position: { line: 21, column: 72 } } - }); - expect(deps[5]).is.eql({ - name: { value: 'github.com/regen-network/protobuf1', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.2', position: { line: 22, column: 83 } } - }); - expect(deps[6]).is.eql({ - name: { value: 'github.com/vmihailenco/msgpack/v5', position: { line: 0, column: 0 } }, - version: { value: 'v5.0.0-beta.1', position: { line: 11, column: 45 } } - }); - expect(deps[7]).is.eql({ - name: { value: 'github.com/btcsuite/btcd', position: { line: 0, column: 0 } }, - version: { value: 'v0.20.1-beta', position: { line: 24, column: 99 } } - }); - }); + require github.com/pmezard/go-difflib v1.0.0 - it('tests single line replace statement in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - go 1.13 require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible + github.com/davecgh/go-spew v1.1.1 ) - replace github.com/alecthomas/units => github.com/test-user/units v13.3.2 - `); - expect(deps.length).equal(2); - expect(deps[0]).is.eql({ - name: { value: 'github.com/test-user/units', position: { line: 0, column: 0 } }, - version: { value: 'v13.3.2', position: { line: 9, column: 75 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v2.5.2-alpha+incompatible', position: { line: 6, column: 34 } } - }); - }); - - it('tests multiple line replace statement in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - go 1.13 - - replace github.com/alecthomas/units => github.com/test-user/units v13.3.2 - - require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible - github.com/davecgh/go-spew v1.1.1+incompatible - github.com/pmezard/go-difflib v1.3.0 - ) - - replace github.com/pierrec/lz4 v2.5.2-alpha+incompatible => github.com/pierrec/lz4 v2.5.3 - replace github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.2 - // replace github.com/pmezard/go-difflib v1.3.0 => github.com/pmezard/go-difflib v1.3.1 - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/test-user/units', position: { line: 0, column: 0 } }, - version: { value: 'v13.3.2', position: { line: 5, column: 75 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v2.5.3', position: { line: 14, column: 92 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.2', position: { line: 15, column: 74 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.0', position: { line: 11, column: 41 } } - }); - }); - - - it('tests multiple module points to same replace module in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin go 1.13 - require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible - github.com/gogo/protobuf v1.3.0 - github.com/golang/protobuf v1.3.4 - ) - replace ( - github.com/golang/protobuf => github.com/gogo/protobuf v1.3.1 - github.com/gogo/protobuf v1.3.0 => github.com/gogo/protobuf v1.3.1 - ) - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, - version: { value: 'v0.1.3-alpha', position: { line: 5, column: 39 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v2.5.2-alpha+incompatible', position: { line: 6, column: 34 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/gogo/protobuf', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.1', position: { line: 12, column: 71 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/gogo/protobuf', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.1', position: { line: 11, column: 66 } } - }); - }); - - it('tests replace block before require in go.mod', async () => { - const deps = await provider.collect(` - module github.com/alecthomas/kingpin - go 1.13 - - replace ( - github.com/alecthomas/units => github.com/test-user/units v13.3.2 - github.com/pierrec/lz4 v2.5.2-alpha+incompatible => github.com/pierrec/lz4 v2.5.3 - github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.2 - // replace github.com/pmezard/go-difflib v1.3.0 => github.com/pmezard/go-difflib v1.3.1 - ) - - require ( - github.com/alecthomas/units v0.1.3-alpha - github.com/pierrec/lz4 v2.5.2-alpha+incompatible - github.com/davecgh/go-spew v1.1.1+incompatible - github.com/pmezard/go-difflib v1.3.0 - ) - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/test-user/units', position: { line: 0, column: 0 } }, - version: { value: 'v13.3.2', position: { line: 6, column: 69 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, - version: { value: 'v2.5.3', position: { line: 7, column: 86 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, - version: { value: 'v1.1.2', position: { line: 8, column: 68 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.0', position: { line: 16, column: 41 } } + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, + version: { value: 'v1.0.0', position: { line: 4, column: 47 } } + }, + { + name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, + version: { value: 'v1.1.1', position: { line: 7, column: 40 } } + } + ]); + }); + + it('tests go.mod with the same package but different versions', async () => { + const deps = await dependencyProvider.collect(` + module test/data/sample1 + + go 1.15 + + require ( + github.com/googleapis/gax-go v1.0.3 + github.com/googleapis/gax-go/v2 v2.0.5 + ) + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/googleapis/gax-go', position: { line: 0, column: 0 } }, + version: { value: 'v1.0.3', position: { line: 7, column: 46 } } + }, + { + name: { value: 'github.com/googleapis/gax-go/v2', position: { line: 0, column: 0 } }, + version: { value: 'v2.0.5', position: { line: 8, column: 49 } } + } + ]); + }); + + it('tests go.mod with comments', async () => { + const deps = await dependencyProvider.collect(` + // This is the starting point. + module github.com/alecthomas/kingpin + require ( + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // Valid data before this. + // Extra comment in require section. + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 + ) + go 1.13 + // Final notes. + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 5, column: 45 } } + }, + { + name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, + version: { value: 'v1.0.0', position: { line: 7, column: 47 } } + }, + { + name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, + version: { value: 'v1.2.2', position: { line: 8, column: 45 } } + } + ]); }); - }); - it('tests go.mod with a module and package of different version', async () => { - const deps = await provider.collect(` - module test/data/sample1 + it('tests empty lines in go.mod', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin - go 1.15 + require ( - require ( - github.com/googleapis/gax-go v1.0.3 - github.com/googleapis/gax-go/v2 v2.0.5 - ) - `); - expect(deps.length).equal(2); - expect(deps[0]).is.eql({ - name: { value: 'github.com/googleapis/gax-go', position: { line: 0, column: 0 } }, - version: { value: 'v1.0.3', position: { line: 7, column: 38 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/googleapis/gax-go/v2', position: { line: 0, column: 0 } }, - version: { value: 'v2.0.5', position: { line: 8, column: 41 } } - }); - }); + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf - it('tests exclude statements in go.mod', async () => { - const deps = await provider.collect(` - module test/data/sample1 + github.com/stretchr/testify v1.2.2 - go 1.15 - require ( - github.com/googleapis/gax-go v1.0.3 - github.com/googleapis/gax-go/v2 v2.0.5 - github.com/gogo/protobuf v1.3.0 - github.com/golang/protobuf v1.3.4 - ) - exclude ( - github.com/googleapis/gax-go v1.0.3 - github.com/googleapis/gax-go/v2 v2.0.5 - ) + ) + go 1.13 - exclude github.com/gogo/protobuf v1.3.0 - - `); - expect(deps.length).equal(4); - expect(deps[0]).is.eql({ - name: { value: 'github.com/googleapis/gax-go', position: { line: 0, column: 0 } }, - version: { value: 'v1.0.3', position: { line: 7, column: 38 } } - }); - expect(deps[1]).is.eql({ - name: { value: 'github.com/googleapis/gax-go/v2', position: { line: 0, column: 0 } }, - version: { value: 'v2.0.5', position: { line: 8, column: 41 } } - }); - expect(deps[2]).is.eql({ - name: { value: 'github.com/gogo/protobuf', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.0', position: { line: 9, column: 34 } } - }); - expect(deps[3]).is.eql({ - name: { value: 'github.com/golang/protobuf', position: { line: 0, column: 0 } }, - version: { value: 'v1.3.4', position: { line: 10, column: 36 } } + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 6, column: 45 } } + }, + { + name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, + version: { value: 'v1.2.2', position: { line: 8, column: 45 } } + } + ]); + }); + + it('tests deps with spaces before and after dep name and version', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin + require ( + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 // indirect + ) + go 1.13 + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 4, column: 48 } } + }, + { + name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, + version: { value: 'v1.1.1', position: { line: 5, column: 48 } } + }, + { + name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, + version: { value: 'v1.0.0', position: { line: 6, column: 47 } } + }, + { + name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, + version: { value: 'v1.2.2', position: { line: 7, column: 52 } } + } + ]); + }); + + it('tests deps with alpha, beta and extra for version', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin + + require ( + github.com/alecthomas/units v0.1.3-alpha + github.com/pierrec/lz4 v2.5.2-alpha+incompatible + github.com/davecgh/go-spew v1.1.1+incompatible + github.com/pmezard/go-difflib v1.3.0+version + github.com/stretchr/testify v1.2.2+incompatible-version + github.com/regen-network/protobuf v1.3.2-alpha.regen.4 + github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 + github.com/btcsuite/btcd v0.20.1-beta + ) + + go 1.13 + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/alecthomas/units', position: { line: 0, column: 0 } }, + version: { value: 'v0.1.3-alpha', position: { line: 5, column: 45 } } + }, + { + name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, + version: { value: 'v2.5.2-alpha+incompatible', position: { line: 6, column: 40 } } + }, + { + name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, + version: { value: 'v1.1.1+incompatible', position: { line: 7, column: 44 } } + }, + { + name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, + version: { value: 'v1.3.0+version', position: { line: 8, column: 47 } } + }, + { + name: { value: 'github.com/stretchr/testify', position: { line: 0, column: 0 } }, + version: { value: 'v1.2.2+incompatible-version', position: { line: 9, column: 45 } } + }, + { + name: { value: 'github.com/regen-network/protobuf', position: { line: 0, column: 0 } }, + version: { value: 'v1.3.2-alpha.regen.4', position: { line: 10, column: 51 } } + }, + { + name: { value: 'github.com/vmihailenco/msgpack/v5', position: { line: 0, column: 0 } }, + version: { value: 'v5.0.0-beta.1', position: { line: 11, column: 51 } } + }, + { + name: { value: 'github.com/btcsuite/btcd', position: { line: 0, column: 0 } }, + version: { value: 'v0.20.1-beta', position: { line: 12, column: 42 } } + } + ]); + }); + + it('tests replace statements in go.mod', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin + go 1.13 + + replace ( + github.com/alecthomas/units v0.1.3-alpha => github.com/test-user/units v13.3.2 // Required by OLM + github.com/alecthomas/units v0.1.3 => github.com/test-user/units v13.3.2 // Required by OLM + github.com/pierrec/lz4 => github.com/pierrec/lz4 v3.4.2 // Required by prometheus-operator + github.com/pierrec/lz4 v3.4.1 => github.com/pierrec/lz4 v3.4.3 // same-module with diff version in replace + ) + + replace github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 => ./msgpack/v5 // replace with local module + + require ( + github.com/alecthomas/units v0.1.3-alpha + github.com/pierrec/lz4 v2.5.2-alpha+incompatible + github.com/davecgh/go-spew v1.1.1+incompatible + github.com/pmezard/go-difflib v1.3.0 + github.com/stretchr/testify v1.2.2+incompatible-version + github.com/regen-network/protobuf v1.3.2-alpha.regen.4 + github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 + github.com/btcsuite/btcd v0.0.0-20151022065526-2efee857e7cf + ) + + replace ( + github.com/davecgh/go-spew v1.1.1+incompatible => github.com/davecgh/go-spew v1.1.2 + github.com/stretchr/testify => github.com/stretchr-1/testify v1.2.3 // test with module and with one import package + github.com/regen-network/protobuf => github.com/regen-network/protobuf1 v1.3.2 // test with module and with one import package + github.com/pmezard/go-difflib v1.3.0 => github.com/pmezard/go-difflib v0.0.0-20151022065526-2efee857e7cf // semver to pseudo + ) + + replace github.com/btcsuite/btcd v0.0.0-20151022065526-2efee857e7cf => github.com/btcsuite/btcd v0.20.1-beta // pseudo to semver + `); + expect(deps).is.eql([ + { + name: { value: 'github.com/test-user/units', position: { line: 0, column: 0 } }, + version: { value: 'v13.3.2', position: { line: 6, column: 88 } } + }, + { + name: { value: 'github.com/pierrec/lz4', position: { line: 0, column: 0 } }, + version: { value: 'v3.4.2', position: { line: 8, column: 66 } } + }, + { + name: { value: 'github.com/davecgh/go-spew', position: { line: 0, column: 0 } }, + version: { value: 'v1.1.2', position: { line: 26, column: 94 } } + }, + { + name: { value: 'github.com/pmezard/go-difflib', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20151022065526-2efee857e7cf', position: { line: 29, column: 87 } } + }, + { + name: { value: 'github.com/stretchr-1/testify', position: { line: 0, column: 0 } }, + version: { value: 'v1.2.3', position: { line: 27, column: 78 } } + }, + { + name: { value: 'github.com/regen-network/protobuf1', position: { line: 0, column: 0 } }, + version: { value: 'v1.3.2', position: { line: 28, column: 89 } } + }, + { + name: { value: 'github.com/vmihailenco/msgpack/v5', position: { line: 0, column: 0 } }, + version: { value: 'v5.0.0-beta.1', position: { line: 21, column: 51 } } + }, + { + name: { value: 'github.com/btcsuite/btcd', position: { line: 0, column: 0 } }, + version: { value: 'v0.20.1-beta', position: { line: 32, column: 109 } } + } + ]); + }); + + it('tests multiple module points to same replace module in go.mod', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin + go 1.13 + + require ( + cloud.google.com/go v0.100.2 + github.com/gogo/protobuf v1.3.0 + github.com/golang/protobuf v1.3.4 + ) + + replace ( + github.com/golang/protobuf => github.com/gogo/protobuf v1.3.1 + github.com/gogo/protobuf v1.3.0 => github.com/gogo/protobuf v1.3.1 + ) + `); + expect(deps).is.eql([ + { + name: { value: 'cloud.google.com/go', position: { line: 0, column: 0 } }, + version: { value: 'v0.100.2', position: { line: 6, column: 37 } } + }, + { + name: { value: 'github.com/gogo/protobuf', position: { line: 0, column: 0 } }, + version: { value: 'v1.3.1', position: { line: 13, column: 77 } } + }, + { + name: { value: 'github.com/gogo/protobuf', position: { line: 0, column: 0 } }, + version: { value: 'v1.3.1', position: { line: 12, column: 72 } } + } + ]); + }); + + it('tests exclude statements in go.mod', async () => { + const deps = await dependencyProvider.collect(` + module github.com/alecthomas/kingpin + go 1.13 + + exclude ( + golang.org/x/crypto v1.4.5 + ) + + exclude github.com/gogo/protobuf + + require ( + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + cloud.google.com/go/compute v1.6.1 + ) + + exclude ( + cloud.google.com/go v0.100.2 + ) + + exclude golang.org/x/sys v1.6.7 + `); + expect(deps).is.eql([ + { + name: { value: 'golang.org/x/sys', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20220728004956-3c1f35247d10', position: { line: 12, column: 34 } } + }, + { + name: { value: 'golang.org/x/sync', position: { line: 0, column: 0 } }, + version: { value: 'v0.0.0-20210220032951-036812b2e83c', position: { line: 13, column: 35 } } + }, + { + name: { value: 'cloud.google.com/go/compute', position: { line: 0, column: 0 } }, + version: { value: 'v1.6.1', position: { line: 14, column: 45 } } + } + ]); }); - }); }); + diff --git a/test/providers/package.json.test.ts b/test/providers/package.json.test.ts index 8e2e3b26..482e4595 100644 --- a/test/providers/package.json.test.ts +++ b/test/providers/package.json.test.ts @@ -1,135 +1,179 @@ +'use strict'; + import { expect } from 'chai'; +import * as sinon from 'sinon'; import { DependencyProvider } from '../../src/providers/package.json'; -describe('NPM package.json parser test', () => { - const collector = new DependencyProvider(); +describe('Javascript NPM package.json parser test', () => { + let dependencyProvider: DependencyProvider; + + beforeEach(() => { + dependencyProvider = new DependencyProvider(); + }); it('tests empty package.json file content', async () => { - const deps = await collector.collect(``); + const deps = await dependencyProvider.collect(``); expect(deps.length).equal(0); }); it('tests empty package.json', async () => { - const deps = await collector.collect(`{}`); + const deps = await dependencyProvider.collect(`{}`); expect(deps.length).equal(0); }); it('tests invalid token package.json', async () => { - const deps = await collector.collect(` - { - <<<<< - "dependencies": { - "hello": "1.0", - } - } + const deps = await dependencyProvider.collect(` + { + <<<<< + "dependencies": { + "hello": "1.0", + } + } `); expect(deps).eql([]); }); it('tests invalid package.json', async () => { - const deps = await collector.collect(` - { - "dependencies": { - "hello": "1.0", - } - } + const deps = await dependencyProvider.collect(` + { + "dependencies": { + "hello": "1.0", + } + } `); expect(deps).eql([]); }); it('tests empty dependencies key', async () => { - const deps = await collector.collect(` - { - "hello":[], - "dependencies": {} - } + const deps = await dependencyProvider.collect(` + { + "hello":[], + "dependencies": {} + } `); - expect(deps.length).equal(0); + expect(deps).eql([]); }); it('tests single dependency ', async () => { - const deps = await collector.collect(`{ - "hello":{}, - "dependencies": { - "hello": "1.0" - } - }`); - expect(deps).is.eql([{ - name: {value: "hello", position: {line: 4, column: 14}}, - version: {value: "1.0", position: {line: 4, column: 23}} - }]); + const deps = await dependencyProvider.collect(` + { + "hello":{}, + "dependencies": { + "hello": "1.0" + } + } + `); + expect(deps).is.eql([ + { + name: {value: "hello", position: {line: 5, column: 22}}, + version: {value: "1.0", position: {line: 5, column: 31}} + } + ]); }); - it('tests single dependency as devDependencies', async () => { - let collector = new DependencyProvider(["devDependencies"]); - let deps = await collector.collect(`{ - "devDependencies": { - "hello": "1.0" - }, - "dependencies": { - "foo": "10.1.1" - } - }`); - expect(deps).is.eql([{ - name: {value: "hello", position: {line: 3, column: 14}}, - version: {value: "1.0", position: {line: 3, column: 23}} - }]); - - collector = new DependencyProvider(["devDependencies", "dependencies"]); - deps = await collector.collect(`{ - "devDependencies": { - "hello": "1.0" - }, - "dependencies": { - "foo": "10.1.1" - } - }`); - expect(deps).is.eql([{ - name: {value: "hello", position: {line: 3, column: 14}}, - version: {value: "1.0", position: {line: 3, column: 23}} - },{ - name: {value: "foo", position: {line: 6, column: 14}}, - version: {value: "10.1.1", position: {line: 6, column: 21}} - }]); + it('tests dependency in devDependencies class', async () => { + dependencyProvider.classes = ["devDependencies"]; + let deps = await dependencyProvider.collect(` + { + "devDependencies": { + "hello": "1.0" + }, + "dependencies": { + "foo": "10.1.1" + } + } + `); + expect(deps).is.eql([ + { + name: {value: "hello", position: {line: 4, column: 22}}, + version: {value: "1.0", position: {line: 4, column: 31}} + } + ]); }); + it('tests dependency in multiple classes', async () => { + dependencyProvider.classes = ["devDependencies", "dependencies"]; + let deps = await dependencyProvider.collect(` + { + "devDependencies": { + "hello": "1.0" + }, + "dependencies": { + "foo": "10.1.1" + } + } + `); + expect(deps).is.eql([ + { + name: {value: "hello", position: {line: 4, column: 22}}, + version: {value: "1.0", position: {line: 4, column: 31}} + }, + { + name: {value: "foo", position: {line: 7, column: 22}}, + version: {value: "10.1.1", position: {line: 7, column: 29}} + } + ]); + }); - it('tests single dependency with version in next line', async () => { - const deps = await collector.collect(`{ - "hello":{}, - "dependencies": { - "hello": - "1.0" - } - }`); - expect(deps).is.eql([{ - name: {value: "hello", position: {line: 4, column: 14}}, - version: {value: "1.0", position: {line: 5, column: 16}} - }]); + + it('tests dependency with version in next line', async () => { + const deps = await dependencyProvider.collect(` + { + "hello":{}, + "dependencies": { + "hello": + "1.0" + } + } + `); + expect(deps).is.eql([ + { + name: {value: "hello", position: {line: 5, column: 22}}, + version: {value: "1.0", position: {line: 6, column: 22}} + } + ]); }); it('tests 3 dependencies with spaces', async () => { - const deps = await collector.collect(`{ - "hello":{}, - "dependencies": { - "hello": "1.0", - "world":"^1.0", - - - "foo": - - " 10.0.1" - } - }`); - expect(deps).is.eql([{ - name: {value: "hello", position: {line: 4, column: 13}}, - version: {value: "1.0", position: {line: 4, column: 37}} - },{ - name: {value: "world", position: {line: 5, column: 16}}, - version: {value: "^1.0", position: {line: 5, column: 24}} - },{ - name: {value: "foo", position: {line: 8, column: 10}}, - version: {value: " 10.0.1", position: {line: 10, column: 12}} - }]); + const deps = await dependencyProvider.collect(` + { + "hello":{}, + "dependencies": { + "hello": "1.0", + "world":"^1.0", + + + "foo": + + " 10.0.1" + } + } + `); + expect(deps).is.eql([ + { + name: {value: "hello", position: {line: 5, column: 18}}, + version: {value: "1.0", position: {line: 5, column: 42}} + }, + { + name: {value: "world", position: {line: 6, column: 22}}, + version: {value: "^1.0", position: {line: 6, column: 30}} + }, + { + name: {value: "foo", position: {line: 9, column: 18}}, + version: {value: " 10.0.1", position: {line: 11, column: 18}} + } + ]); + }); + + it('should throw an error for invalid JSON content', async () => { + sinon.stub(dependencyProvider, 'parseJson').throws(new Error('Mock error message')); + + try { + await dependencyProvider.collect(``); + throw new Error('Expected an error to be thrown'); + } catch (error) { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Mock error message'); + } }); }); diff --git a/test/providers/pom.xml.test.ts b/test/providers/pom.xml.test.ts index 24269919..f6ca02ca 100644 --- a/test/providers/pom.xml.test.ts +++ b/test/providers/pom.xml.test.ts @@ -3,242 +3,262 @@ import { expect } from 'chai'; import { DependencyProvider } from '../../src/providers/pom.xml'; -describe('Maven pom.xml parser test', () => { +describe('Java Maven pom.xml parser test', () => { + let dependencyProvider: DependencyProvider; + + beforeEach(() => { + dependencyProvider = new DependencyProvider(); + }); it('tests pom.xml with empty string', async () => { - const pom = ` - `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const pom = ``; + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); it('tests pom.xml with empty project', async () => { - const pom = ` + const pom = ` + - + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); - it('tests pom.xml with empty project + dependencies', async () => { - const pom = ` - + it('tests pom.xml with empty dependencies', async () => { + const pom = ` + + - - + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); it('tests valid pom.xml', async () => { const pom = ` - - - - c - ab-cd - 2.3 - test - - - foo - bar - 2.4 - - - + + + + c + ab-cd + 2.3 + test + + + foo + bar + 2.4 + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'foo/bar', position: { line: 10, column: 17 } }, - version: { value: '2.4', position: { line: 13, column: 30 } }, - }]); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'foo/bar', position: { line: 10, column: 21 } }, + version: { value: '2.4', position: { line: 13, column: 34 } }, + } + ]); }); - it('highlights duplicate dependencies', async () => { + it('tests duplicate dependencies', async () => { const pom = ` - - - - c - ab-cd - 2.3 - test - - - foo - bar - 2.4 - - - foo - bar - 2.4 - - - + + + + c + ab-cd + 2.3 + test + + + foo + bar + 2.4 + + + foo + bar + 2.4 + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'foo/bar', position: { line: 10, column: 17 } }, - version: { value: '2.4', position: { line: 13, column: 30 } }, - },{ - name: { value: 'foo/bar', position: { line: 15, column: 17 } }, - version: { value: '2.4', position: { line: 18, column: 30 } }, - }]); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'foo/bar', position: { line: 10, column: 21 } }, + version: { value: '2.4', position: { line: 13, column: 34 } }, + }, + { + name: { value: 'foo/bar', position: { line: 15, column: 21 } }, + version: { value: '2.4', position: { line: 18, column: 34 } }, + } + ]); }); - it('highlights duplicate dependencies when one has version', async () => { + it('tests duplicate dependencies when only one does not specify a version', async () => { const pom = ` - - - - c - ab-cd - 2.3 - test - - - foo - bar - 2.4 - - - foo - bar - - - + + + + c + ab-cd + 2.3 + test + + + foo + bar + 2.4 + + + foo + bar + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'foo/bar', position: { line: 10, column: 17 } }, - version: { value: '2.4', position: { line: 13, column: 30 } }, - },{ - name: { value: 'foo/bar', position: { line: 15, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: ` - foo - bar - __VERSION__ - `, - range: { - end: { - character: 29, - line: 17 - }, - start: { - character: 16, - line: 14 + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'foo/bar', position: { line: 10, column: 21 } }, + version: { value: '2.4', position: { line: 13, column: 34 } }, + }, + { + name: { value: 'foo/bar', position: { line: 15, column: 21 } }, + context: { value: + ` + foo + bar + __VERSION__ + `, + range: { + end: { + character: 33, + line: 17 + }, + start: { + character: 20, + line: 14 + } } } } - }]); + ]); }); - it('highlights duplicate dependencies when none has version', async () => { + it('test duplicate dependencies where none specify a version', async () => { const pom = ` - - - - c - ab-cd - 2.3 - test - - - foo - bar - - - foo - bar - - - + + + + c + ab-cd + 2.3 + test + + + foo + bar + + + foo + bar + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'foo/bar', position: { line: 10, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: ` - foo - bar - __VERSION__ - `, - range: { - end: { - character: 29, - line: 12 - }, - start: { - character: 16, - line: 9 + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'foo/bar', position: { line: 10, column: 21 } }, + context: { value: + ` + foo + bar + __VERSION__ + `, + range: { + end: { + character: 33, + line: 12 + }, + start: { + character: 20, + line: 9 + } } } - } - },{ - name: { value: 'foo/bar', position: { line: 14, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: - ` - foo - bar - __VERSION__ - `, - range: { - end: { - character: 29, - line: 16 - }, - start: { - character: 16, - line: 13 + }, + { + name: { value: 'foo/bar', position: { line: 14, column: 21 } }, + context: { value: + ` + foo + bar + __VERSION__ + `, + range: { + end: { + character: 33, + line: 16 + }, + start: { + character: 20, + line: 13 + } } } } - }]); + ]); }); it('tests pom.xml with multiple dependencies', async () => { const pom = ` - - + + + + + plugins + a + 2.3 + + + - plugins + dep a - 2.3 + 10.1 + + + foo + bar + 2.4 - - - - dep - a - 10.1 - - - foo - bar - 2.4 - - - + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'plugins/a', position: { line: 5, column: 21 } }, - version: { value: '2.3', position: { line: 8, column: 34 } } - }, { - name: { value: 'dep/a', position: { line: 13, column: 17 } }, - version: { value: '10.1', position: { line: 16, column: 30 } } - }, { - name: { value: 'foo/bar', position: { line: 18, column: 17 } }, - version: { value: '2.4', position: { line: 21, column: 30 } } - }]); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'plugins/a', position: { line: 5, column: 25 } }, + version: { value: '2.3', position: { line: 8, column: 38 } } + }, + { + name: { value: 'dep/a', position: { line: 13, column: 21 } }, + version: { value: '10.1', position: { line: 16, column: 34 } } + }, + { + name: { value: 'foo/bar', position: { line: 18, column: 21 } }, + version: { value: '2.4', position: { line: 21, column: 34 } } + } + ]); }); it('tests pom.xml with only test scope', async () => { @@ -270,228 +290,234 @@ describe('Maven pom.xml parser test', () => { `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); it('tests pom.xml with invalid dependencies', async () => { const pom = ` - - - - - ab-cd - - - c - - - - c - - - - - - + + + + + ab-cd + + + c + + + + c + + + + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); it('tests pom.xml with invalid dependency versions', async () => { const pom = ` - - - - c - ab-cd - - - - c - ab-cd - - - - + + + + c + ab-cd + + + + c + ab-cd + + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'c/ab-cd', position: { line: 4, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: - ` - c - ab-cd - __VERSION__ - `, - range: { - end: { - character: 29, - line: 7 - }, - start: { - character: 16, - line: 3 + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'c/ab-cd', position: { line: 4, column: 21 } }, + context: { value: + ` + c + ab-cd + __VERSION__ + `, + range: { + end: { + character: 33, + line: 7 + }, + start: { + character: 20, + line: 3 + } } } - } - },{ - name: { value: 'c/ab-cd', position: { line: 9, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: - ` - c - ab-cd - __VERSION__ - `, - range: { - end: { - character: 30, - line: 11 - }, - start: { - character: 16, - line: 8 + }, + { + name: { value: 'c/ab-cd', position: { line: 9, column: 21 } }, + context: { value: + ` + c + ab-cd + __VERSION__ + `, + range: { + end: { + character: 34, + line: 11 + }, + start: { + character: 20, + line: 8 + } } } } - }]); + ]); }); it('tests pom.xml with dependencyManagement scope', async () => { const pom = ` - - - - - {a.groupId} - bc - {a.version} - runtime - - - - a - b-c - 1.2.3 - compile - true - - - - - c - ab-cd - 2.3 - - - foo - bar - 2.4 - - - + + + + + {a.groupId} + bc + {a.version} + runtime + + + + a + b-c + 1.2.3 + compile + true + + + + + c + ab-cd + 2.3 + + + foo + bar + 2.4 + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'c/ab-cd', position: { line: 21, column: 17 } }, - version: { value: '2.3', position: { line: 24, column: 30 } } - }, { - name: { value: 'foo/bar', position: { line: 26, column: 17 } }, - version: { value: '2.4', position: { line: 29, column: 30 } } - }]); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'c/ab-cd', position: { line: 21, column: 21 } }, + version: { value: '2.3', position: { line: 24, column: 34 } } + }, + { + name: { value: 'foo/bar', position: { line: 26, column: 21 } }, + version: { value: '2.4', position: { line: 29, column: 34 } } + } + ]); }); it('tests pom.xml without version and with properties', async () => { const pom = ` - - - - c - ab-cd - 2.3 - - - \${some.example} - \${other.example} - - - c - ab-other - - - + + + + c + ab-cd + 2.3 + + + \${some.example} + \${other.example} + + + c + ab-other + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps).is.eql([{ - name: { value: 'c/ab-cd', position: { line: 4, column: 17 } }, - version: { value: '2.3', position: { line: 7, column: 30 } } - }, { - name: { value: '${some.example}/${other.example}', position: { line: 9, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: ` - \${some.example} - \${other.example} - __VERSION__ - `, - range: { - end: { - character: 29, - line: 11 - }, - start: { - character: 16, - line: 8 + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([ + { + name: { value: 'c/ab-cd', position: { line: 4, column: 21 } }, + version: { value: '2.3', position: { line: 7, column: 34 } } + }, + { + name: { value: '${some.example}/${other.example}', position: { line: 9, column: 21 } }, + context: { value: ` + \${some.example} + \${other.example} + __VERSION__ + `, + range: { + end: { + character: 33, + line: 11 + }, + start: { + character: 20, + line: 8 + } } } - } - }, { - name: { value: 'c/ab-other', position: { line: 13, column: 17 } }, - version: { value: '', position: { line: 0, column: 0 } }, - context: { value: ` - c - ab-other - __VERSION__ - `, - range: { - end: { - character: 29, - line: 15 - }, - start: { - character: 16, - line: 12 + }, + { + name: { value: 'c/ab-other', position: { line: 13, column: 21 } }, + context: { value: ` + c + ab-other + __VERSION__ + `, + range: { + end: { + character: 33, + line: 15 + }, + start: { + character: 20, + line: 12 + } } } } - }]); + ]); }); it('tests pom.xml with only dependencyManagement scope', async () => { const pom = ` - - - - - {a.groupId} - bc - {a.version} - runtime - - - - a - b-c - 1.2.3 - compile - true - - - + + + + + {a.groupId} + bc + {a.version} + runtime + + + + a + b-c + 1.2.3 + compile + true + + + `; - const deps = await new DependencyProvider().collect(pom); - expect(deps.length).equal(0); + const deps = await dependencyProvider.collect(pom); + expect(deps).is.eql([]); }); }); diff --git a/test/providers/requirements.txt.test.ts b/test/providers/requirements.txt.test.ts index 11cc4555..64078f34 100644 --- a/test/providers/requirements.txt.test.ts +++ b/test/providers/requirements.txt.test.ts @@ -1,80 +1,98 @@ +'use strict'; + import { expect } from 'chai'; import { DependencyProvider } from '../../src/providers/requirements.txt'; -describe('PyPi requirements.txt parser test', () => { - const collector = new DependencyProvider(); +describe('Python PyPi requirements.txt parser test', () => { + let dependencyProvider: DependencyProvider; + + beforeEach(() => { + dependencyProvider = new DependencyProvider(); + }); + + it('tests empty requirements.txt', async () => { + const deps = await dependencyProvider.collect(``); + expect(deps).is.eql([]); + }); it('tests valid requirements.txt', async () => { - const deps = await collector.collect(`a==1 + const deps = await dependencyProvider.collect(` + a==1 B==2.1.1 c>=10.1 d<=20.1.2.3.4.5.6.7.8 `); - expect(deps).is.eql([{ - name: {value: 'a', position: {line: 0, column: 0}}, - version: {value: '1', position: {line: 1, column: 4}} - },{ - name: {value: 'b', position: {line: 0, column: 0}}, - version: {value: '2.1.1', position: {line: 2, column: 16}} - },{ - name: {value: 'c', position: {line: 0, column: 0}}, - version: {value: '10.1', position: {line: 3, column: 16}} - },{ - name: {value: 'd', position: {line: 0, column: 0}}, - version: {value: '20.1.2.3.4.5.6.7.8', position: {line: 4, column: 16}} - }]); + expect(deps).is.eql([ + { + name: {value: 'a', position: {line: 0, column: 0}}, + version: {value: '1', position: {line: 2, column: 16}} + }, + { + name: {value: 'b', position: {line: 0, column: 0}}, + version: {value: '2.1.1', position: {line: 3, column: 16}} + }, + { + name: {value: 'c', position: {line: 0, column: 0}}, + version: {value: '10.1', position: {line: 4, column: 16}} + }, + { + name: {value: 'd', position: {line: 0, column: 0}}, + version: {value: '20.1.2.3.4.5.6.7.8', position: {line: 5, column: 16}} + } + ]); }); it('tests requirements.txt with comments', async () => { - const deps = await collector.collect(`# hello world + const deps = await dependencyProvider.collect(` + # hello world a==1 # hello - # another comment b==2.1.1 - c # yet another comment >=10.1 + # invalid line b==2.1.1 + c # invalid line >=10.1 d<=20.1.2.3.4.5.6.7.8 # done `); - expect(deps).is.eql([{ - name: {value: 'a', position: {line: 0, column: 0}}, - version: {value: '1', position: {line: 2, column: 16}} - },{ - name: {value: 'c', position: {line: 0, column: 0}}, - version: {value: '', position: {line: 4, column: 1}} // column shouldn't matter for empty versions - },{ - name: {value: 'd', position: {line: 0, column: 0}}, - version: {value: '20.1.2.3.4.5.6.7.8', position: {line: 5, column: 16}} - }]); - }); - - it('tests empty requirements.txt', async () => { - const deps = await collector.collect(``); - expect(deps).is.eql([]); + expect(deps).is.eql([ + { + name: {value: 'a', position: {line: 0, column: 0}}, + version: {value: '1', position: {line: 3, column: 16}} + }, + { + name: {value: 'd', position: {line: 0, column: 0}}, + version: {value: '20.1.2.3.4.5.6.7.8', position: {line: 6, column: 16}} + } + ]); }); it('tests empty lines', async () => { - const deps = await collector.collect(` + const deps = await dependencyProvider.collect(` a==1 `); - expect(deps).is.eql([{ - name: {value: 'a', position: {line: 0, column: 0}}, - version: {value: '1', position: {line: 3, column: 16}} - }]); + expect(deps).is.eql([ + { + name: {value: 'a', position: {line: 0, column: 0}}, + version: {value: '1', position: {line: 3, column: 16}} + } + ]); }); it('tests deps with spaces before and after comparators', async () => { - const deps = await collector.collect(` + const deps = await dependencyProvider.collect(` a ==1 - b <= 10.1 + b <= 10.1 `); - expect(deps).is.eql([{ - name: {value: 'a', position: {line: 0, column: 0}}, - version: {value: '1', position: {line: 2, column: 24}} - },{ - name: {value: 'b', position: {line: 0, column: 0}}, - version: {value: '10.1', position: {line: 4, column: 35}} - }]); + expect(deps).is.eql([ + { + name: {value: 'a', position: {line: 0, column: 0}}, + version: {value: '1', position: {line: 2, column: 24}} + }, + { + name: {value: 'b', position: {line: 0, column: 0}}, + version: {value: '10.1', position: {line: 4, column: 33}} + } + ]); }); }); diff --git a/test/vulnerability.test.ts b/test/vulnerability.test.ts index 1e0581b6..9fdfd4a9 100644 --- a/test/vulnerability.test.ts +++ b/test/vulnerability.test.ts @@ -1,131 +1,107 @@ +'use strict'; + import { expect } from 'chai'; -import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver'; -import { DependencyProvider as PomXml } from '../src/providers/pom.xml'; -import { Vulnerability, ANALYTICS_SOURCE } from '../src/vulnerability'; +import { Range } from 'vscode-languageserver'; +import { DependencyProvider } from '../src/providers/pom.xml'; +import { Vulnerability } from '../src/vulnerability'; describe('Vulnerability tests', () => { - const dummyRange: Range = { + const mavenDependencyProvider: DependencyProvider = new DependencyProvider(); + const mockMavenRef: string = 'pkg:maven/mockGroupId1/mockArtifact1@mockVersion'; + const mockRange: Range = { start: { - line: 3, - character: 4 + line: 123, + character: 123 }, end: { - line: 3, - character: 10 + line: 456, + character: 456 } }; - // it('Test vulnerability with minimal fields/without vulnerabilities and without recommendations', async () => { - // let vulnerability = new Vulnerability( - // dummyRange - // ); - - // const msg = '\nRecommendation: No RedHat packages to recommend'; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Information, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - // expect(vulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - // }); - - // it('Test vulnerability without vulnerabilities and with recommendations', async () => { - // let vulnerability = new Vulnerability( - // dummyRange, - // 0, - // 'MockRef', - // {name: 'mockRecommendationName', version: 'mockRecommendationVersion'}, - // 'mockRecommendationName', - // 'mockRecommendationVersion', - // null, - // '' - // ); - - // const msg = 'MockRef\nRecommendation: mockRecommendationName:mockRecommendationVersion'; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Information, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - // expect(vulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - // }); - - // it('Test vulnerability with vulnerabilities and without remediations', async () => { - // let vulnerability = new Vulnerability( - // dummyRange, - // 1, - // 'MockRef', - // null, - // '', - // '', - // null, - // 'MockSeverity' - // ); - - // const msg = "MockRef\nKnown security vulnerabilities: 1\nHighest severity: MockSeverity\nHas remediation: No"; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Error, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - // expect(vulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - // }); - - // it('Test vulnerability with vulnerabilities and with remediations', async () => { - // let vulnerability = new Vulnerability( - // dummyRange, - // 1, - // 'MockRef', - // null, - // '', - // '', - // {mockCVE: 'mockCVE'}, - // 'MockSeverity' - // ); - - // const msg = "MockRef\nKnown security vulnerabilities: 1\nHighest severity: MockSeverity\nHas remediation: Yes"; - // let expectedDiagnostic: Diagnostic = { - // severity: DiagnosticSeverity.Error, - // range: dummyRange, - // message: msg, - // source: ANALYTICS_SOURCE, - // }; - - // expect(vulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); - // }); - - it('Test vulnerability with minimal fields and without vulnerabilities', async () => { + class MockDependencyData { + constructor( + public sourceId: string, + public issuesCount: number, + public highestVulnerabilitySeverity: string + ) {} + } + + it('should return diagnostic without vulnerabilities from single source', async () => { + const mockDependencyData: MockDependencyData[] = [ + new MockDependencyData('snyk-snyk', 0, 'NONE') + ]; let vulnerability = new Vulnerability( - dummyRange, - 'pkg:maven/MockPkg@1.2.3' - ); - - expect(vulnerability.getDiagnostic()).to.eql(undefined); + mavenDependencyProvider, + mockRange, + mockMavenRef, + mockDependencyData + ); + + const diagnostic = vulnerability.getDiagnostic(); + expect(diagnostic.message.replace(/\s/g, "")).to.eql(` + mockGroupId1/mockArtifact1@mockVersion + + snyk-snyk vulnerability info: + Known security vulnerabilities: 0 + Highest severity: NONE + `.replace(/\s/g, "")); }); - it('Test vulnerability with vulnerabilities', async () => { + it('should return diagnostic with vulnerabilities from single source', async () => { + const mockDependencyData: MockDependencyData[] = [ + new MockDependencyData('snyk-snyk', 2, 'HIGH') + ]; let vulnerability = new Vulnerability( - dummyRange, - 'pkg:maven/MockPkg@1.2.3', - 1, - 'MockSeverity' - ); - vulnerability.provider = new PomXml(); - - const msg = "MockRef\nKnown security vulnerabilities: 1\nHighest severity: MockSeverity"; - let expectedDiagnostic: Diagnostic = { - severity: DiagnosticSeverity.Error, - range: dummyRange, - message: msg, - source: ANALYTICS_SOURCE, - }; + mavenDependencyProvider, + mockRange, + mockMavenRef, + mockDependencyData + ); + + const diagnostic = vulnerability.getDiagnostic(); + expect(diagnostic.message.replace(/\s/g, "")).to.eql(` + mockGroupId1/mockArtifact1@mockVersion + + snyk-snyk vulnerability info: + Known security vulnerabilities: 2 + Highest severity: HIGH + `.replace(/\s/g, "")); + }); - expect(vulnerability.getDiagnostic().toString().replace(/\s/g, "")).is.eql(expectedDiagnostic.toString().replace(/\s/g, "")); + it('should return diagnostic from multiple source', async () => { + const mockDependencyData: MockDependencyData[] = [ + new MockDependencyData('snyk-snyk', 1, 'HIGH'), + new MockDependencyData('snyk-oss', 2, 'LOW'), + new MockDependencyData('oss-oss', 0, 'NONE'), + new MockDependencyData('oss-snyk', 5, 'HIGH') + ]; + let vulnerability = new Vulnerability( + mavenDependencyProvider, + mockRange, + mockMavenRef, + mockDependencyData + ); + + const diagnostic = vulnerability.getDiagnostic(); + expect(diagnostic.message.replace(/\s/g, "")).to.eql(` + mockGroupId1/mockArtifact1@mockVersion + + snyk-snyk vulnerability info: + Known security vulnerabilities: 1 + Highest severity: HIGH + + snyk-oss vulnerability info: + Known security vulnerabilities: 2 + Highest severity: LOW + + oss-oss vulnerability info: + Known security vulnerabilities: 0 + Highest severity: NONE + + oss-snyk vulnerability info: + Known security vulnerabilities: 5 + Highest severity: HIGH + `.replace(/\s/g, "")); }); });