From 166bbbe7140f2b42bd2cf0c160ead947767e4f7a Mon Sep 17 00:00:00 2001 From: Agustin Groh Date: Tue, 10 Dec 2024 10:13:13 -0300 Subject: [PATCH] chore:SP-1927 Adds undeclared component unit tests --- __tests__/copyleft-argument-builder.test.ts | 10 ++--- __tests__/copyleft-policy-check.test.ts | 6 +-- __tests__/undeclared-argument-builder.test.ts | 45 +++++++++++++++++++ __tests__/undeclared-policy-check.test.ts | 30 +++++++------ .../argument_builders/argument-builder.ts | 23 ++++++++++ .../copyleft-argument-builder.ts | 23 ++++++++++ .../undeclared-argument-builder.ts | 43 ++++++++++++++++++ src/policies/undeclared-policy-check.ts | 26 +++-------- 8 files changed, 166 insertions(+), 40 deletions(-) create mode 100644 __tests__/undeclared-argument-builder.test.ts create mode 100644 src/policies/argument_builders/undeclared-argument-builder.ts diff --git a/__tests__/copyleft-argument-builder.test.ts b/__tests__/copyleft-argument-builder.test.ts index aa7ad1c..7f9cf24 100644 --- a/__tests__/copyleft-argument-builder.test.ts +++ b/__tests__/copyleft-argument-builder.test.ts @@ -21,7 +21,7 @@ describe('CopyleftArgumentBuilder', () => { (COPYLEFT_LICENSE_INCLUDE as any) = defaultCopyleftLicenseInclude; }); - test('Copyleft explicit test', async () => { + it('Copyleft explicit test', async () => { (COPYLEFT_LICENSE_EXPLICIT as any) = 'MIT,Apache-2.0'; (COPYLEFT_LICENSE_EXCLUDE as any) = 'MIT,Apache-2.0'; (REPO_DIR as any) = 'scanoss'; @@ -45,7 +45,7 @@ describe('CopyleftArgumentBuilder', () => { ]); }); - test('Copyleft exclude test', async () => { + it('Copyleft exclude test', async () => { (COPYLEFT_LICENSE_EXCLUDE as any) = 'MIT,Apache-2.0'; (REPO_DIR as any) = 'scanoss'; (OUTPUT_FILEPATH as any) = 'results.json'; @@ -67,7 +67,7 @@ describe('CopyleftArgumentBuilder', () => { ]); }); - test('Copyleft include test', async () => { + it('Copyleft include test', async () => { (COPYLEFT_LICENSE_INCLUDE as any) = 'MIT,Apache-2.0,LGPL-3.0-only'; (REPO_DIR as any) = 'scanoss'; (OUTPUT_FILEPATH as any) = 'results.json'; @@ -89,7 +89,7 @@ describe('CopyleftArgumentBuilder', () => { ]); }); - test('Copyleft empty parameters test', async () => { + it('Copyleft empty parameters test', async () => { (REPO_DIR as any) = 'scanoss'; (OUTPUT_FILEPATH as any) = 'results.json'; const builder = new CopyLeftArgumentBuilder(); @@ -108,7 +108,7 @@ describe('CopyleftArgumentBuilder', () => { ]); }); - test('Build Command test', async () => { + it('Build Command test', async () => { (REPO_DIR as any) = 'scanoss'; (OUTPUT_FILEPATH as any) = 'results.json'; const builder = new CopyLeftArgumentBuilder(); diff --git a/__tests__/copyleft-policy-check.test.ts b/__tests__/copyleft-policy-check.test.ts index fc356b0..4b9270d 100644 --- a/__tests__/copyleft-policy-check.test.ts +++ b/__tests__/copyleft-policy-check.test.ts @@ -44,7 +44,7 @@ describe('CopyleftPolicyCheck', () => { (COPYLEFT_LICENSE_INCLUDE as any) = defaultCopyleftLicenseInclude; }); - test('Copyleft policy check fail', async () => { + it('Copyleft policy check fail', async () => { const TEST_DIR = __dirname; const TEST_REPO_DIR = path.join(TEST_DIR, 'data'); const TEST_RESULTS_FILE = 'results.json'; @@ -64,7 +64,7 @@ describe('CopyleftPolicyCheck', () => { expect(copyleftPolicyCheck.conclusion).toEqual(CONCLUSION.Neutral); }, 30000); - test('Copyleft policy empty results', async () => { + it('Copyleft policy empty results', async () => { const TEST_DIR = __dirname; const TEST_REPO_DIR = path.join(TEST_DIR, 'data'); const TEST_RESULTS_FILE = 'results.json'; @@ -85,7 +85,7 @@ describe('CopyleftPolicyCheck', () => { expect(copyleftPolicyCheck.conclusion).toEqual(CONCLUSION.Success); }, 30000); - test('Copyleft policy explicit licenses', async () => { + it('Copyleft policy explicit licenses', async () => { const TEST_DIR = __dirname; const TEST_REPO_DIR = path.join(TEST_DIR, 'data'); const TEST_RESULTS_FILE = 'results.json'; diff --git a/__tests__/undeclared-argument-builder.test.ts b/__tests__/undeclared-argument-builder.test.ts new file mode 100644 index 0000000..bbbb1d8 --- /dev/null +++ b/__tests__/undeclared-argument-builder.test.ts @@ -0,0 +1,45 @@ +import { OUTPUT_FILEPATH, REPO_DIR, RUNTIME_CONTAINER, SCANOSS_SETTINGS } from '../src/app.input'; +import { UndeclaredArgumentBuilder } from '../src/policies/argument_builders/undeclared-argument-builder'; + +describe('UndeclaredArgumentBuilder', () => { + it('Build Command test', async function () { + (REPO_DIR as any) = 'repodir'; + (OUTPUT_FILEPATH as any) = 'results.json'; + const builder = new UndeclaredArgumentBuilder(); + const cmd = await builder.build(); + expect(cmd).toEqual([ + 'run', + '-v', + 'repodir:/scanoss', + RUNTIME_CONTAINER, + 'inspect', + 'undeclared', + '--input', + 'results.json', + '--format', + 'md', + '--sbom-format', + 'legacy' + ]); + }); + + it('Build Command style scanoss.json', async function () { + (REPO_DIR as any) = 'repodir'; + (OUTPUT_FILEPATH as any) = 'results.json'; + (SCANOSS_SETTINGS as any) = true; + const builder = new UndeclaredArgumentBuilder(); + const cmd = await builder.build(); + expect(cmd).toEqual([ + 'run', + '-v', + 'repodir:/scanoss', + RUNTIME_CONTAINER, + 'inspect', + 'undeclared', + '--input', + 'results.json', + '--format', + 'md' + ]); + }); +}); diff --git a/__tests__/undeclared-policy-check.test.ts b/__tests__/undeclared-policy-check.test.ts index b3d5093..1294784 100644 --- a/__tests__/undeclared-policy-check.test.ts +++ b/__tests__/undeclared-policy-check.test.ts @@ -2,8 +2,8 @@ import { CONCLUSION, PolicyCheck } from '../src/policies/policy-check'; import { ScannerResults } from '../src/services/result.interfaces'; import { resultsMock } from './results.mock'; import { UndeclaredPolicyCheck } from '../src/policies/undeclared-policy-check'; -import * as sbomUtils from '../src/utils/sbom.utils'; -import { sbomMock } from './sbom.mock'; +import { OUTPUT_FILEPATH, REPO_DIR } from '../src/app.input'; +import path from 'path'; // Mock the @actions/github module jest.mock('@actions/github', () => ({ @@ -40,24 +40,28 @@ describe('UndeclaredPolicyCheck', () => { }); it('should pass the policy check when undeclared components are not found', async () => { - jest.spyOn(sbomUtils, 'parseSBOM').mockImplementation(async () => Promise.resolve(sbomMock[1])); + const TEST_DIR = __dirname; + const TEST_REPO_DIR = path.join(TEST_DIR, 'data'); + const TEST_RESULTS_FILE = 'empty-results.json'; - await undeclaredPolicyCheck.run(scannerResults); + // Set the required environment variables + (REPO_DIR as any) = TEST_REPO_DIR; + (OUTPUT_FILEPATH as any) = TEST_RESULTS_FILE; + + await undeclaredPolicyCheck.run(); expect(undeclaredPolicyCheck.conclusion).toEqual(CONCLUSION.Success); }); it('should fail the policy check when undeclared components are found', async () => { - jest.spyOn(sbomUtils, 'parseSBOM').mockImplementation(async () => Promise.resolve(sbomMock[0])); + const TEST_DIR = __dirname; + const TEST_REPO_DIR = path.join(TEST_DIR, 'data'); + const TEST_RESULTS_FILE = 'results.json'; - await undeclaredPolicyCheck.run(scannerResults); - expect(undeclaredPolicyCheck.conclusion).toEqual(CONCLUSION.Neutral); - }); + // Set the required environment variables + (REPO_DIR as any) = TEST_REPO_DIR; + (OUTPUT_FILEPATH as any) = TEST_RESULTS_FILE; - it('should exceeded the max limit', async () => { - jest.spyOn(sbomUtils, 'parseSBOM').mockImplementation(async () => Promise.resolve(sbomMock[0])); - scannerResults = JSON.parse(resultsMock[6].content); - await undeclaredPolicyCheck.run(scannerResults); - // Neutral = Failure on test environment + await undeclaredPolicyCheck.run(); expect(undeclaredPolicyCheck.conclusion).toEqual(CONCLUSION.Neutral); }); }); diff --git a/src/policies/argument_builders/argument-builder.ts b/src/policies/argument_builders/argument-builder.ts index fd4c8af..9c19292 100644 --- a/src/policies/argument_builders/argument-builder.ts +++ b/src/policies/argument_builders/argument-builder.ts @@ -1,3 +1,26 @@ +// SPDX-License-Identifier: MIT +/* + Copyright (c) 2024, SCANOSS + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + export abstract class ArgumentBuilder { abstract build(): Promise; } diff --git a/src/policies/argument_builders/copyleft-argument-builder.ts b/src/policies/argument_builders/copyleft-argument-builder.ts index 32e6857..944e7ee 100644 --- a/src/policies/argument_builders/copyleft-argument-builder.ts +++ b/src/policies/argument_builders/copyleft-argument-builder.ts @@ -1,3 +1,26 @@ +// SPDX-License-Identifier: MIT +/* + Copyright (c) 2024, SCANOSS + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + import { ArgumentBuilder } from './argument-builder'; import { COPYLEFT_LICENSE_EXCLUDE, diff --git a/src/policies/argument_builders/undeclared-argument-builder.ts b/src/policies/argument_builders/undeclared-argument-builder.ts new file mode 100644 index 0000000..769cee7 --- /dev/null +++ b/src/policies/argument_builders/undeclared-argument-builder.ts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +/* + Copyright (c) 2024, SCANOSS + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +import { ArgumentBuilder } from './argument-builder'; +import { OUTPUT_FILEPATH, REPO_DIR, RUNTIME_CONTAINER, SCANOSS_SETTINGS } from '../../app.input'; + +export class UndeclaredArgumentBuilder extends ArgumentBuilder { + async build(): Promise { + return [ + 'run', + '-v', + `${REPO_DIR}:/scanoss`, + RUNTIME_CONTAINER, + 'inspect', + 'undeclared', + '--input', + OUTPUT_FILEPATH, + '--format', + 'md', + ...(!SCANOSS_SETTINGS ? ['--sbom-format', 'legacy'] : []) // Sets sbom format output to legacy if SCANOSS_SETTINGS is not true + ]; + } +} diff --git a/src/policies/undeclared-policy-check.ts b/src/policies/undeclared-policy-check.ts index 5e9a700..b9f4133 100644 --- a/src/policies/undeclared-policy-check.ts +++ b/src/policies/undeclared-policy-check.ts @@ -24,8 +24,10 @@ import { PolicyCheck } from './policy-check'; import { CHECK_NAME } from '../app.config'; import * as core from '@actions/core'; -import { EXECUTABLE, OUTPUT_FILEPATH, REPO_DIR, RUNTIME_CONTAINER, SCANOSS_SETTINGS } from '../app.input'; +import { EXECUTABLE } from '../app.input'; import * as exec from '@actions/exec'; +import { UndeclaredArgumentBuilder } from './argument_builders/undeclared-argument-builder'; +import { ArgumentBuilder } from './argument_builders/argument-builder'; /** * Verifies that all components identified in scanner results are declared in the project's SBOM. @@ -36,30 +38,16 @@ import * as exec from '@actions/exec'; */ export class UndeclaredPolicyCheck extends PolicyCheck { static policyName = 'Undeclared Policy'; - constructor() { + private argumentBuilder: ArgumentBuilder; + constructor(argumentBuilder: ArgumentBuilder = new UndeclaredArgumentBuilder()) { super(`${CHECK_NAME}: ${UndeclaredPolicyCheck.policyName}`); - } - - private buildArgs(): string[] { - return [ - 'run', - '-v', - `${REPO_DIR}:/scanoss`, - RUNTIME_CONTAINER, - 'inspect', - 'undeclared', - '--input', - OUTPUT_FILEPATH, - '--format', - 'md', - ...(!SCANOSS_SETTINGS ? ['--sbom-format', 'legacy'] : []) // Sets sbom format output to legacy if SCANOSS_SETTINGS is not true - ]; + this.argumentBuilder = argumentBuilder; } async run(): Promise { core.info(`Running Undeclared Components Policy Check...`); super.initStatus(); - const args = this.buildArgs(); + const args = await this.argumentBuilder.build(); core.debug(`Args: ${args}`); const options = { failOnStdErr: false,