Unit Test (#12)

* annoying tests..

* update test

* udpate

* update the tests
This commit is contained in:
Eugene
2024-02-28 17:22:13 -08:00
committed by GitHub
parent 57dd0cd8d8
commit 69180bebd9
6 changed files with 264 additions and 7 deletions
+6 -7
View File
@@ -41,17 +41,17 @@ jobs:
- name: Lint
id: npm-lint
run: npm run lint
# - name: Test
# id: npm-ci-test
# run: npm run ci-test
- name: Test
id: npm-ci-test
run: npm run ci-test
test-attest-sbom-with-local-sbom-file:
name: Test attest-sbom action with local sbom file
runs-on: ubuntu-latest
permissions:
contents: read
contents: write
id-token: write
packages: write
steps:
- name: Checkout
@@ -71,9 +71,8 @@ jobs:
name: Test attest-sbom action
runs-on: ubuntu-latest
permissions:
contents: read
contents: write
id-token: write
packages: write
steps:
- name: Checkout
+17
View File
@@ -0,0 +1,17 @@
/**
* Unit tests for the action's entrypoint, src/index.ts
*/
import * as main from '../src/main'
// Mock the action's entrypoint
const runMock = jest.spyOn(main, 'run').mockImplementation()
describe('index', () => {
it('calls run when imported', async () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('../src/index')
expect(runMock).toHaveBeenCalled()
})
})
+112
View File
@@ -0,0 +1,112 @@
import * as core from '@actions/core'
import * as main from '../src/main'
import * as fs from 'fs'
import os from 'os'
import * as path from 'path'
// Mock the GitHub Actions core library
jest.mock('@actions/core')
const getInputMock = jest.spyOn(core, 'getInput')
const setOutputMock = jest.spyOn(core, 'setOutput')
const setFailedMock = jest.spyOn(core, 'setFailed')
// Ensure that setFailed doesn't set an exit code during tests
setFailedMock.mockImplementation(() => {})
describe('SBOM Action', () => {
let tempDir = '/'
let outputs = {} as Record<string, string>
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sbom'))
jest.resetAllMocks()
setOutputMock.mockImplementation((key, value) => {
outputs[key] = value
})
})
afterEach(() => {
fs.rmdirSync(tempDir, { recursive: true })
outputs = {}
})
it('successfully processes an SBOM', async () => {
const spdxSBOM = JSON.stringify({
spdxVersion: 'SPDX-2.2',
SPDXID: 'SPDXRef-DOCUMENT',
packages: []
})
const filePath = path.join(tempDir, 'spdxSBOM.json')
fs.writeFileSync(filePath, spdxSBOM)
const inputs = {
'sbom-path': filePath
}
getInputMock.mockImplementation(mockInput(inputs))
const originalEnv = process.env
process.env = { ...originalEnv, RUNNER_TEMP: '/tmp' }
// Run the main function
await main.run()
// Verify that outputs were set correctly
expect(setOutputMock).toHaveBeenCalledTimes(2)
expect(setOutputMock).toHaveBeenNthCalledWith(
2,
'predicate-type',
'https://spdx.dev/Document/v2.2'
)
expect(outputs['predicate-path']).toBeTruthy()
const predicatePath = outputs['predicate-path']
// Verify that the temporary file exists
expect(fs.existsSync(predicatePath)).toBe(true)
// Read the content of the temporary file
const fileContent = fs.readFileSync(predicatePath, 'utf-8')
// Verify that the content matches the predicate params
expect(JSON.parse(fileContent)).toEqual(JSON.parse(spdxSBOM))
// Clean up the temporary file
fs.unlinkSync(predicatePath)
process.env = originalEnv
})
it('fails when an error occurs without input', async () => {
await main.run()
expect(setFailedMock).toHaveBeenCalledWith(
'TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received undefined'
)
})
it('fails when an error occurs with wrong sbom format', async () => {
const spdxSBOM = JSON.stringify({
SPDXID: 'SPDXRef-DOCUMENT'
})
const filePath = path.join(tempDir, 'spdxSBOM.json')
fs.writeFileSync(filePath, spdxSBOM)
const inputs = {
'sbom-path': filePath
}
getInputMock.mockImplementation(mockInput(inputs))
const originalEnv = process.env
process.env = { ...originalEnv, RUNNER_TEMP: '/tmp' }
// Run the main function
await main.run()
expect(setFailedMock).toHaveBeenCalledWith('Unsupported SBOM format')
})
})
function mockInput(inputs: Record<string, string>): typeof core.getInput {
return (name: string): string => {
if (name in inputs) {
return inputs[name]
}
return ''
}
}
+125
View File
@@ -0,0 +1,125 @@
import {
storePredicate,
parseSBOMFromPath,
generateSBOMPredicate,
SBOM
} from '../src/sbom'
import type { Predicate } from '@actions/attest'
import * as fs from 'fs'
import os from 'os'
import * as path from 'path'
describe('parseSBOMFromPath', () => {
let tempDir = '/'
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sbom'))
})
afterEach(() => {
fs.rmdirSync(tempDir, { recursive: true })
})
it('correctly parses an SPDX file', async () => {
const spdxSBOM = JSON.stringify({
spdxVersion: 'SPDX-2.2',
SPDXID: 'SPDXRef-DOCUMENT'
})
const filePath = path.join(tempDir, 'spdxSBOM.json')
fs.writeFileSync(filePath, spdxSBOM)
await expect(parseSBOMFromPath(filePath)).resolves.toEqual({
type: 'spdx',
object: JSON.parse(spdxSBOM)
})
})
it('correctly parses a CycloneDX file', async () => {
const cycloneDXSBOM = JSON.stringify({
bomFormat: 'CycloneDX',
serialNumber: '123',
specVersion: '1.2'
})
const filePath = path.join(tempDir, 'cyclonedxSBOM.json')
fs.writeFileSync(filePath, cycloneDXSBOM)
await expect(parseSBOMFromPath(filePath)).resolves.toEqual({
type: 'cyclonedx',
object: JSON.parse(cycloneDXSBOM)
})
})
it('throws an error for unsupported SBOM formats', async () => {
const filePath = path.join(tempDir, 'random.json')
fs.writeFileSync(filePath, JSON.stringify({ random: 'value' }))
await expect(parseSBOMFromPath(filePath)).rejects.toThrow(
'Unsupported SBOM format'
)
})
})
describe('storePredicate', () => {
it('should store the predicate to a temporary file', () => {
const predicate = { params: { key: 'value' } } as Predicate
// Mocking the process.env['RUNNER_TEMP'] value
const originalEnv = process.env
process.env = { ...originalEnv, RUNNER_TEMP: '/tmp' }
const tempFile = storePredicate(predicate)
// Verify that the temporary file exists
expect(fs.existsSync(tempFile)).toBe(true)
// Read the content of the temporary file
const fileContent = fs.readFileSync(tempFile, 'utf-8')
// Verify that the content matches the predicate params
expect(JSON.parse(fileContent)).toEqual(predicate.params)
// Clean up the temporary file
fs.unlinkSync(tempFile)
// Restore the original process.env
process.env = originalEnv
})
it('should throw an error if RUNNER_TEMP environment variable is missing', () => {
const predicate = { params: { key: 'value' } } as Predicate
// Mocking the process.env['RUNNER_TEMP'] value
const originalEnv = process.env
process.env = {}
// Verify that an error is thrown
expect(() => storePredicate(predicate)).toThrow(
'Missing RUNNER_TEMP environment variable'
)
// Restore the original process.env
process.env = originalEnv
})
})
describe('generateSBOMPredicate', () => {
it('generates SPDX predicate correctly', () => {
const sbom = { type: 'spdx', object: { spdxVersion: 'SPDX-2.2' } } as SBOM
const result = generateSBOMPredicate(sbom)
expect(result.type).toContain('https://spdx.dev/Document/v2.2')
expect(result.params).toEqual(sbom.object)
})
it('generates CycloneDX predicate correctly', () => {
const sbom = { type: 'cyclonedx', object: {} } as SBOM
const result = generateSBOMPredicate(sbom)
expect(result.type).toEqual('https://cyclonedx.org/bom')
expect(result.params).toEqual(sbom.object)
})
it('throws error for unsupported SBOM formats', () => {
const sbom = { type: 'spdx', object: {} }
// @ts-expect-error test error case
expect(() => generateSBOMPredicate(sbom)).toThrow(
'Cannot find spdxVersion in the SBOM'
)
})
})
+1
View File
@@ -0,0 +1 @@
process.stdout.write = jest.fn()
+3
View File
@@ -46,6 +46,9 @@
"js",
"ts"
],
"setupFilesAfterEnv": [
"./jest.setup.js"
],
"testMatch": [
"**/*.test.ts"
],