more test coverage (#18)
Signed-off-by: Brian DeHamer <[email protected]>
This commit is contained in:
+21
-2
@@ -9,6 +9,7 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import { mockFulcio, mockRekor, mockTSA } from '@sigstore/mock'
|
||||
import * as oci from '@sigstore/oci'
|
||||
import nock from 'nock'
|
||||
import { SEARCH_PUBLIC_GOOD_URL } from '../src/endpoints'
|
||||
import * as main from '../src/main'
|
||||
@@ -43,7 +44,7 @@ describe('action', () => {
|
||||
'base64'
|
||||
)}.}`
|
||||
|
||||
const subjectName = 'subject'
|
||||
const subjectName = 'registry/foo/bar'
|
||||
const subjectDigest =
|
||||
'sha256:7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
||||
const predicate = '{}'
|
||||
@@ -189,6 +190,9 @@ describe('action', () => {
|
||||
})
|
||||
|
||||
describe('when the repository is public', () => {
|
||||
const getRegCredsSpy = jest.spyOn(oci, 'getRegistryCredentials')
|
||||
const attachArtifactSpy = jest.spyOn(oci, 'attachArtifactToImage')
|
||||
|
||||
const inputs = {
|
||||
'subject-digest': subjectDigest,
|
||||
'subject-name': subjectName,
|
||||
@@ -206,13 +210,26 @@ describe('action', () => {
|
||||
|
||||
// Mock the action's inputs
|
||||
getInputMock.mockImplementation(mockInput(inputs))
|
||||
getBooleanInputMock.mockImplementation(() => false)
|
||||
// This is where we mock the push-to-registry input
|
||||
getBooleanInputMock.mockImplementation(() => true)
|
||||
|
||||
await mockFulcio({
|
||||
baseURL: 'https://fulcio.sigstore.dev',
|
||||
strict: false
|
||||
})
|
||||
await mockRekor({ baseURL: 'https://rekor.sigstore.dev' })
|
||||
|
||||
getRegCredsSpy.mockImplementation(() => ({
|
||||
username: 'username',
|
||||
password: 'password'
|
||||
}))
|
||||
attachArtifactSpy.mockImplementation(async () =>
|
||||
Promise.resolve({
|
||||
digest: 'sha256:123456',
|
||||
mediaType: 'application/vnd.cncf.notary.v2',
|
||||
size: 123456
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes the action w/o error', async () => {
|
||||
@@ -220,6 +237,8 @@ describe('action', () => {
|
||||
|
||||
expect(runMock).toHaveReturned()
|
||||
expect(setFailedMock).not.toHaveBeenCalled()
|
||||
expect(getRegCredsSpy).toHaveBeenCalledWith(subjectName)
|
||||
expect(attachArtifactSpy).toHaveBeenCalled()
|
||||
expect(infoMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringMatching(
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import fs from 'fs/promises'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { predicateFromInputs } from '../src/predicate'
|
||||
|
||||
describe('subjectFromInputs', () => {
|
||||
afterEach(() => {
|
||||
process.env['INPUT_PREDICATE'] = ''
|
||||
process.env['INPUT_PREDICATE-PATH'] = ''
|
||||
process.env['INPUT_PREDICATE-TYPE'] = ''
|
||||
})
|
||||
|
||||
describe('when no inputs are provided', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(/predicate-type/i)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when neither predicate path nor predicate are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(
|
||||
/one of predicate-path or predicate must be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when both predicate path and predicate are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-PATH'] = 'path/to/predicate'
|
||||
process.env['INPUT_PREDICATE'] = '{}'
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(() => predicateFromInputs()).toThrow(
|
||||
/only one of predicate-path or predicate may be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate path', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
const content = '{}'
|
||||
|
||||
beforeEach(async () => {
|
||||
// Set-up temp directory
|
||||
const tmpDir = await fs.realpath(os.tmpdir())
|
||||
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
|
||||
// Write file to temp directory
|
||||
await fs.writeFile(path.join(dir, filename), content)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean-up temp directory
|
||||
await fs.rm(dir, { recursive: true })
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE-PATH'] = path.join(dir, filename)
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('returns the predicate', () => {
|
||||
expect(predicateFromInputs()).toEqual({
|
||||
type: 'https://example.com/predicate',
|
||||
params: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a predicate value', () => {
|
||||
const content = '{}'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_PREDICATE'] = content
|
||||
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
|
||||
})
|
||||
|
||||
it('returns the predicate', () => {
|
||||
expect(predicateFromInputs()).toEqual({
|
||||
type: 'https://example.com/predicate',
|
||||
params: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,205 @@
|
||||
import crypto from 'crypto'
|
||||
import fs from 'fs/promises'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { subjectFromInputs } from '../src/subject'
|
||||
|
||||
describe('subjectFromInputs', () => {
|
||||
afterEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = ''
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = ''
|
||||
process.env['INPUT_SUBJECT-NAME'] = ''
|
||||
})
|
||||
|
||||
describe('when no inputs are provided', () => {
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/one of subject-path or subject-digest must be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when both subject path and subject digest are provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = 'path/to/subject'
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/only one of subject-path or subject-digest may be provided/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when subject digest is provided but not the name', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/subject-name must be provided when using subject-digest/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a subject digest', () => {
|
||||
const name = 'subject'
|
||||
|
||||
describe('when the digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the alogrithm is not supported', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'md5:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the sha256 digest is malformed', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = 'sha256:deadbeef'
|
||||
process.env['INPUT_SUBJECT-NAME'] = 'subject'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/subject-digest must be in the format "sha256:<hex-digest>"/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the sha256 digest is valid', () => {
|
||||
const alg = 'sha256'
|
||||
const digest =
|
||||
'7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-DIGEST'] = `${alg}:${digest}`
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
expect(subject[0].name).toEqual(name)
|
||||
expect(subject[0].digest).toEqual({ [alg]: digest })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a subject path', () => {
|
||||
describe('when the file does NOT exist', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = '/f/a/k/e'
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(subjectFromInputs()).rejects.toThrow(
|
||||
/could not find subject at path/i
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the file eixts', () => {
|
||||
let dir = ''
|
||||
const filename = 'subject'
|
||||
const content = 'file content'
|
||||
|
||||
const expectedDigest = crypto
|
||||
.createHash('sha256')
|
||||
.update(content)
|
||||
.digest('hex')
|
||||
|
||||
beforeEach(async () => {
|
||||
// Set-up temp directory
|
||||
const tmpDir = await fs.realpath(os.tmpdir())
|
||||
dir = await fs.mkdtemp(tmpDir + path.sep)
|
||||
|
||||
// Write file to temp directory
|
||||
await fs.writeFile(path.join(dir, filename), content)
|
||||
|
||||
// Add files for glob testing
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean-up temp directory
|
||||
await fs.rm(dir, { recursive: true })
|
||||
})
|
||||
|
||||
describe('when no name is provided', () => {
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
expect(subject[0].name).toEqual(filename)
|
||||
expect(subject[0].digest).toEqual({ sha256: expectedDigest })
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a name is provided', () => {
|
||||
const name = 'mysubject'
|
||||
|
||||
beforeEach(() => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
|
||||
process.env['INPUT_SUBJECT-NAME'] = name
|
||||
})
|
||||
|
||||
it('returns the subject', async () => {
|
||||
const subject = await subjectFromInputs()
|
||||
|
||||
expect(subject).toBeDefined()
|
||||
expect(subject).toHaveLength(1)
|
||||
expect(subject[0].name).toEqual(name)
|
||||
expect(subject[0].digest).toEqual({ sha256: expectedDigest })
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a file glob is supplied', () => {
|
||||
beforeEach(async () => {
|
||||
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, 'subject-*')
|
||||
})
|
||||
|
||||
it('returns the multiple subjects', async () => {
|
||||
const subjects = await subjectFromInputs()
|
||||
|
||||
expect(subjects).toBeDefined()
|
||||
expect(subjects).toHaveLength(3)
|
||||
|
||||
/* eslint-disable-next-line github/array-foreach */
|
||||
subjects.forEach((subject, i) => {
|
||||
expect(subject.name).toEqual(`${filename}-${i}`)
|
||||
expect(subject.digest).toEqual({ sha256: expectedDigest })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user