diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f9961f..40cb5ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,7 @@ name: Continuous Integration on: + workflow_dispatch: pull_request: push: branches: diff --git a/__tests__/copyleft-policy-check.test.ts b/__tests__/copyleft-policy-check.test.ts new file mode 100644 index 0000000..7708e96 --- /dev/null +++ b/__tests__/copyleft-policy-check.test.ts @@ -0,0 +1,32 @@ +import { CopyleftPolicyCheck } from '../src/policies/copyleft-policy-check'; +import { CONCLUSION, PolicyCheck } from '../src/policies/policy-check'; +import { ScannerResults } from '../src/services/result.interfaces'; +import * as github from '@actions/github'; +import { resultsMock } from './results.mock'; + +describe('CopyleftPolicyCheck', () => { + let scannerResults: ScannerResults; + let policyCheck: CopyleftPolicyCheck; + + beforeEach(() => { + jest.clearAllMocks(); + + jest.spyOn(github, 'getOctokit').mockImplementation(); + jest.spyOn(PolicyCheck.prototype, 'run').mockImplementation(); + jest.spyOn(PolicyCheck.prototype, 'finish').mockImplementation(); + + policyCheck = new CopyleftPolicyCheck(); + }); + + it('should pass the policy check when no copyleft components are found', async () => { + scannerResults = JSON.parse(resultsMock[0].content); + await policyCheck.run(scannerResults); + expect(policyCheck.conclusion).toEqual(CONCLUSION.Success); + }); + + it('should fail the policy check when copyleft components are found', async () => { + scannerResults = JSON.parse(resultsMock[2].content); + await policyCheck.run(scannerResults); + expect(policyCheck.conclusion).toEqual(CONCLUSION.Neutral); + }); +}); diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index f167390..fc88eeb 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -30,7 +30,7 @@ describe('action', () => { setOutputMock = jest.spyOn(core, 'setOutput').mockImplementation(); }); - it('sets the time output', async () => { + it('SCANOSS Scan Action started', async () => { // Set the action's inputs as return values from core.getInput() getInputMock.mockImplementation((name: string): string => { switch (name) { diff --git a/__tests__/report-service.test.ts b/__tests__/report-service.test.ts index 43a83d5..189c835 100644 --- a/__tests__/report-service.test.ts +++ b/__tests__/report-service.test.ts @@ -1,6 +1,7 @@ import * as github from '@actions/github'; +import * as core from '@actions/core'; -import { generatePRSummary } from '../src/services/report.service'; +import { generateJobSummary, generatePRSummary } from '../src/services/report.service'; const tableTest = [ { @@ -654,7 +655,7 @@ const tableTest = [ } ]; -describe('Test report service', () => { +describe('Test report service: generatePRSummary', () => { beforeEach(() => { jest.spyOn(github.context, 'repo', 'get').mockReturnValue({ owner: 'x', repo: 'y' }); github.context.runId = 0; @@ -663,8 +664,22 @@ describe('Test report service', () => { for (const t of tableTest) { it(`${t.name}`, () => { const report = generatePRSummary(JSON.parse(t.scannerResults), []); - console.log(report); expect(report).toEqual(t.output); }); } }); + +describe('Test report service: generateJobSummary', () => { + beforeEach(() => { + jest.spyOn(github.context, 'repo', 'get').mockReturnValue({ owner: 'x', repo: 'y' }); + jest.spyOn(core.summary, 'write').mockImplementation(); + + github.context.runId = 0; + }); + + for (const t of tableTest) { + it(`${t.name}`, async () => { + await expect(generateJobSummary(JSON.parse(t.scannerResults), [])).resolves.toEqual(undefined); + }); + } +}); diff --git a/__tests__/sbom.mock.ts b/__tests__/sbom.mock.ts new file mode 100644 index 0000000..134e720 --- /dev/null +++ b/__tests__/sbom.mock.ts @@ -0,0 +1,20 @@ +import { Sbom } from '../src/utils/sbom.utils'; + +export const sbomMock: Sbom[] = [ + { + components: [] // empty sbom + }, + { + components: [ + { purl: 'pkg:github/scanoss/engine' }, + { purl: 'pkg:github/scanoss/engine' }, + { purl: 'pkg:github/scanoss/engine' }, + { purl: 'pkg:pypi/requests' }, + { purl: 'pkg:pypi/crc32c' }, + { purl: 'pkg:pypi/binaryornot' }, + { purl: 'pkg:pypi/pytest' }, + { purl: 'pkg:pypi/pytest-cov' }, + { purl: 'pkg:pypi/beautifulsoup4' } + ] + } +]; diff --git a/__tests__/undeclared-policy-check.test.ts b/__tests__/undeclared-policy-check.test.ts new file mode 100644 index 0000000..01a755d --- /dev/null +++ b/__tests__/undeclared-policy-check.test.ts @@ -0,0 +1,39 @@ +import { CONCLUSION, PolicyCheck } from '../src/policies/policy-check'; +import { ScannerResults } from '../src/services/result.interfaces'; +import * as github from '@actions/github'; +import { resultsMock } from './results.mock'; +import { UndeclaredPolicyCheck } from '../src/policies/undeclared-policy-check'; +import * as sbom from '../src/utils/sbom.utils'; +import { sbomMock } from './sbom.mock'; + +describe('UndeclaredPolicyCheck', () => { + let scannerResults: ScannerResults; + let undeclaredPolicyCheck: UndeclaredPolicyCheck; + + beforeEach(() => { + jest.clearAllMocks(); + + jest.spyOn(github, 'getOctokit').mockImplementation(); + + jest.spyOn(PolicyCheck.prototype, 'run').mockImplementation(); + jest.spyOn(PolicyCheck.prototype, 'finish').mockImplementation(); + + scannerResults = JSON.parse(resultsMock[3].content); + + undeclaredPolicyCheck = new UndeclaredPolicyCheck(); + }); + + it('should pass the policy check when undeclared components are not found', async () => { + jest.spyOn(sbom, 'parseSbom').mockImplementation(async _ => Promise.resolve(sbomMock[1])); + + await undeclaredPolicyCheck.run(scannerResults); + expect(undeclaredPolicyCheck.conclusion).toEqual(CONCLUSION.Success); + }); + + it('should fail the policy check when undeclared components are found', async () => { + jest.spyOn(sbom, 'parseSbom').mockImplementation(async _ => Promise.resolve(sbomMock[0])); + + await undeclaredPolicyCheck.run(scannerResults); + expect(undeclaredPolicyCheck.conclusion).toEqual(CONCLUSION.Neutral); + }); +}); diff --git a/src/policies/policy-check.ts b/src/policies/policy-check.ts index 3f94f3b..a0b77cc 100644 --- a/src/policies/policy-check.ts +++ b/src/policies/policy-check.ts @@ -94,7 +94,7 @@ export abstract class PolicyCheck { return await this.finish(summary, text); } - protected async finish(summary: string, text?: string): Promise { + async finish(summary: string, text?: string): Promise { core.debug(`Finish policy check: ${this.checkName}. (conclusion=${this._conclusion})`); this._status = STATUS.FINISHED;