build: inline external Github Actions to unblock CI (#12241)

* build: inline cached-dependencies to unblock CI

* Run E2E on pull_request on;y

* Inline all external actions

* Checkout needed for internal actions

Also fixes pre-commit

* Add missing files
This commit is contained in:
Jesse Yang
2021-01-04 04:16:07 -08:00
committed by GitHub
parent 7cc0de1694
commit a3bbbf8ea3
173 changed files with 48871 additions and 78 deletions

View File

@@ -0,0 +1,150 @@
import {setOutput as coreSetOutput, debug as coreDebug} from '@actions/core'
import {writeFileSync} from 'fs'
import {ChangedFiles} from 'typings/ChangedFiles'
import {GitHubFile} from 'typings/GitHubFile'
import {getErrorString} from './UtilsHelper'
/**
* @function sortChangedFiles
* @param files pass in array of GithubFile's to be sorted
* @returns ChangedFiles object that has .files, .added, .modified, .renamed, and .removed
*/
export function sortChangedFiles(files: GitHubFile[]): ChangedFiles {
try {
coreDebug(
`Here are the files I am changing: ${JSON.stringify(files, null, 2)}`
)
const changedFiles = {
files: [],
added: [],
removed: [],
renamed: [],
modified: []
} as ChangedFiles
files.forEach(f => {
changedFiles[f.status].push(
f.filename || f.added || f.removed || f.renamed || f.modified
)
changedFiles.files.push(
f.filename || f.added || f.removed || f.modified || f.renamed
)
})
return changedFiles
} catch (error) {
const eString = `There was an issue sorting files changed.`
throw new Error(
getErrorString(
error.name,
error.status,
sortChangedFiles.name,
eString,
JSON.stringify(error)
)
)
}
}
/**
* @function getFormatExt
* @param format output format 'json' = '.json' ',' = '.csv' anything else is '.txt'.
* @returns file extension, '.json', '.csv', or '.txt'
*/
export function getFormatExt(format: string): string {
let ext
switch (format.trim()) {
case 'json':
ext = '.json'
break
case ',':
ext = '.csv'
break
default:
ext = '.txt'
break
}
return ext
}
/**
* @function formatChangedFiles
* @param format output format 'json' will stringify anything else will files.join('string')
* @param files string list of files to format
* @returns string for output of changedFiles
*/
export function formatChangedFiles(format: string, files: string[]): string {
if (format === 'json') {
return JSON.stringify(files)
}
return files.join(format)
}
/**
* @function writeFiles
* @param format output format 'json' will stringify anything else will files.join('string')
* @param key changedFiles type added, modified, removed, or files
* @param files string list of files to format
* @returns string output to be stored in file
*/
export function writeFiles(format: string, key: string, files: string[]): void {
try {
const ext = getFormatExt(format)
const fileName = key === 'files' ? `${key}${ext}` : `files_${key}${ext}`
coreDebug(
`Writing output file ${
process.env.HOME
}/${fileName} with ${format} and files ${JSON.stringify(files, null, 2)}`
)
writeFileSync(
`${process.env.HOME}/${fileName}`,
formatChangedFiles(format, files),
'utf-8'
)
} catch (error) {
const eString = `There was an issue writing output files.`
throw new Error(
getErrorString(
error.name,
error.status,
writeFiles.name,
eString,
JSON.stringify(error)
)
)
}
}
/**
* @function writeOutput
* @param format output format 'json' will stringify anything else will files.join('string')
* @param key changedFiles type added, modified, removed, or files
* @param files string list of files to format
* @returns string output to be stored to action output
*/
export function writeOutput(
format: string,
key: string,
files: string[]
): void {
try {
const fileName = key === 'files' ? key : `files_${key}`
coreDebug(
`Writing output ${fileName} with ${format} and files ${JSON.stringify(
files,
null,
2
)}`
)
coreSetOutput(fileName, formatChangedFiles(format, files))
} catch (error) {
const eString = `There was an issue setting action outputs.`
throw new Error(
getErrorString(
error.name,
error.status,
writeOutput.name,
eString,
JSON.stringify(error)
)
)
}
}

View File

@@ -0,0 +1,176 @@
import {GitHub} from '@actions/github'
import {GitHubFile} from 'typings/GitHubFile'
import {Inferred} from 'typings/Inferred'
import {getErrorString} from './UtilsHelper'
/**
* @function initClient
* @throws {Error} not sure what might trigger this, but it will throw an error.
* @param token github token to add to client
* @returns authenticated github client
*/
export function initClient(token: string): GitHub {
try {
return new GitHub(token)
} catch (error) {
const eString = `There was an error creating github client. Please check your token.`
throw new Error(
getErrorString(error.name, error.status, initClient.name, eString, error)
)
}
}
/**
* @function getChangedPRFiles
* @throws {Error} when a 404 or other is received. 404 can be bad repo, owner, pr, or unauthenticated
* @param client authenticated github client (possibly un-authenticated if public)
* @param repo repo string. file-changes-action
* @param owner owner string. trilom
* @param pullNumber pr number to get changed files for
* @returns Promise of array of changed files
*/
export async function getChangedPRFiles(
client: GitHub,
repo: string,
owner: string,
pullNumber: number
): Promise<GitHubFile[]> {
try {
const options = client.pulls.listFiles.endpoint.merge({
owner,
repo,
pull_number: pullNumber
})
const files: GitHubFile[] = await client.paginate(
options,
response => response.data
)
return files
} catch (error) {
const eString = `There was an error getting change files for repo:${repo} owner:${owner} pr:${pullNumber}`
let ePayload: string
if (error.name === 'HttpError' && +error.status === 404)
ePayload = getErrorString(
error.name,
error.status,
getChangedPRFiles.name,
eString,
error
)
else
ePayload = getErrorString(
`Unknown Error:${error.name || ''}`,
error.status,
getChangedPRFiles.name,
eString,
error.message
)
throw new Error(ePayload)
}
}
/**
* @function getChangedPushFiles
* @throws {Error} when a 404 or other is received. 404 can be bad repo, owner, sha, or unauthenticated
* @param client authenticated github client (possibly un-authenticated if public)
* @param repo repo string. file-changes-action
* @param owner owner string. trilom
* @param base BASE commit sha to compare
* @param head HEAD commit sha to compare
* @returns Promise of array of changed files
*/
export async function getChangedPushFiles(
client: GitHub,
repo: string,
owner: string,
base: string,
head: string
): Promise<GitHubFile[]> {
try {
const options = client.repos.compareCommits.endpoint.merge({
owner,
repo,
base,
head
})
const files: GitHubFile[] = await client.paginate(
options,
response => response.data.files
)
return files
} catch (error) {
const eString = `There was an error getting change files for repo:${repo} owner:${owner} base:${base} head:${head}`
let ePayload: string
if (error.name === 'HttpError' && +error.status === 404)
ePayload = getErrorString(
error.name,
error.status,
getChangedPushFiles.name,
eString,
error
)
else
ePayload = getErrorString(
`Unknown Error:${error.name || ''}`,
error.status,
getChangedPushFiles.name,
eString,
error.message
)
throw new Error(ePayload)
}
}
/**
* @function getChangedFiles
* @param client client authenticated github client (possibly un-authenticated if public)
* @param repoFull repo owner/repo string. trilom/file-changes-action
* @type {Inferred} pass in iinferred type from inferInput
* @returns Promise of an array of changed PR or push files
*/
export async function getChangedFiles(
client: GitHub,
repoFull: string,
{before, after, pr = NaN}: Inferred
): Promise<GitHubFile[]> {
try {
if (repoFull.split('/').length > 2) {
throw new Error(
getErrorString(
`Bad-Repo`,
500,
'self',
`Repo input of ${repoFull} has more than 2 length after splitting.`
)
)
}
const owner = repoFull.split('/')[0]
const repo = repoFull.split('/')[1]
let files: GitHubFile[] = []
if (Number.isNaN(pr))
files = await getChangedPushFiles(
client,
repo,
owner,
before || '',
after || ''
)
else files = await getChangedPRFiles(client, repo, owner, pr)
return files
} catch (error) {
const pError = JSON.parse(error.message)
if (pError.from.includes('getChanged'))
throw new Error(
JSON.stringify(
{...pError, ...{from: `${error.status}/${error.name}`}},
null,
2
)
)
const eString = `There was an error getting change files outputs pr: ${pr} before: ${before} after: ${after}`
const ePayload: string = getErrorString(
`Unknown Error:${error.name}`,
error.status,
getChangedFiles.name,
eString,
error.message
)
throw new Error(ePayload)
}
}

View File

@@ -0,0 +1,120 @@
import {warning as coreWarning, getInput as coreGetInput} from '@actions/core'
import {context} from '@actions/github'
import {Inferred} from 'typings/Inferred'
import {Inputs} from 'typings/Inputs'
import {getErrorString} from './UtilsHelper'
/**
* @function getInputs
* @description reads the inputs to the action with core.getInput and returns object
* @returns {Inputs} object of inputs for the github action
*/
export function getInputs(): Inputs {
try {
const githubToken =
coreGetInput('githubToken') || process.env.GITHUB_TOKEN || false
if (!githubToken)
throw new Error(
getErrorString(
'getInputs Error',
500,
getInputs.name,
'Received no token, a token is a requirement.'
)
)
let prNumber
if (typeof context.issue.number !== 'undefined') {
if (
+coreGetInput('prNumber') !== context.issue.number &&
coreGetInput('prNumber')
) {
prNumber = +coreGetInput('prNumber')
} else {
prNumber = context.issue.number
}
} else {
prNumber = +coreGetInput('prNumber') || NaN
}
return {
githubRepo:
coreGetInput('githubRepo') ||
`${context.repo.owner}/${context.repo.repo}`,
githubToken,
pushBefore:
coreGetInput('pushBefore') ||
(context.payload.before === undefined ? false : context.payload.before),
pushAfter:
coreGetInput('pushAfter') ||
(context.payload.after === undefined ? false : context.payload.after),
prNumber,
output: coreGetInput('output') || ' ',
fileOutput: coreGetInput('fileOutput') || ' ',
event: context.eventName
} as Inputs
} catch (error) {
const eString = `Received an issue getting action inputs.`
const retVars = Object.fromEntries(
Object.entries(process.env).filter(
key =>
key[0].includes('GITHUB') ||
key[0].includes('INPUT_') ||
key[0] === 'HOME'
)
)
throw new Error(
getErrorString('getInputs Error', 500, getInputs.name, eString, retVars)
)
}
}
/**
* @function inferInput
* @param before BASE commit sha to compare
* @param after HEAD commit sha to compare
* @param pr pr number to get changed files for
* @returns {Inferred} object of inferred input for the action
*/
export function inferInput(
before: string,
after: string,
pr: number
): Inferred {
const event = context.eventName
const weirdInput = `Received event from ${event}, but also received a before(${before}) or after(${after}) value.\n I am assuming you want to use a Push event but forgot something, so I'm giving you a message.`
const allInput = `Received event from ${event}, but received a before(${before}), after(${after}), and PR(${pr}).\n I am assuming you want to use one or the other but I am giving you Push.`
if (event === 'pull_request') {
if (
before &&
after &&
(before !== context.payload.before || after !== context.payload.after)
)
return {before, after} // PR(push) - pull_request event with push inputs | PUSH
if (before || after) coreWarning(weirdInput) // PR(push) - pull_request event with single push input | PR*
return {pr} // PR - pull_request event with no push inputs | PR
}
if (event === 'push') {
if (pr) return {pr} // Push(PR) - push event with pr inputs | PR
return {before, after} // Push - push event with no pr inputs | PUSH
}
if (pr) {
if (before && after) {
coreWarning(allInput) // Not PR or Push - all inputs | PUSH*
if (event === 'issue_comment') return {before, after} // If you explicitly set a before/after in an issue comment it will return those
return {pr} // Not PR or Push - pr inputs | PR if a PR before and after assume its a synchronize and return the whole PR
}
if (before || after) coreWarning(weirdInput) // Not PR or Push - pull_request event with single push input | PR*
return {pr} // Not PR or Push - pr inputs | PR
}
if (before || after) {
if (!(before && after)) {
const eString = `Received event from ${event}, but only received a before(${before}) or after(${after}).\n I need both of these if you want to use a Push event.`
throw new Error(
getErrorString('inferInput Error', 500, inferInput.name, eString)
)
}
return {before, after} // Not PR or Push - push inputs | PUSH
}
const eString = `Received event from ${event}, but received no inputs. {event_name:${event}, pr: ${+pr}, before:${before}, after:${after}}`
throw new Error(
getErrorString('inferInput Error', 500, inferInput.name, eString)
)
}

View File

@@ -0,0 +1,59 @@
import {setFailed} from '@actions/core'
import {ActionError} from 'typings/ActionError'
/**
* @function getErrorString
* @param name name of error
* @param status status code of error
* @param from name of function that error is thrown from
* @param message error message
* @param error error object to stringify and attach
*/
export function getErrorString(
name: string,
status = 500,
from: string,
message: string,
error: any = ''
): string {
try {
const test = JSON.stringify(
{
error: `${status}/${name}`,
from,
message,
payload: error
} as ActionError,
null,
2
)
return test
} catch (error_) {
setFailed(`Error throwing error.\n ${JSON.stringify(error_.message)}`)
throw new Error(
JSON.stringify({name: '500/undefined', message: 'Error throwing error.'})
)
}
}
/**
* @function errorMessage
* @param f name of function
* @param e error object
* @returns error message for function
*/
export function errorMessage(f: string, e: Error): string {
const error = JSON.stringify(e, null, 2)
let ret
if (f.includes('getInputs')) ret = `There was an getting action inputs.`
if (f.includes('inferInput'))
ret = `There was an issue inferring inputs to the action.`
if (f.includes('initClient'))
ret = `There was an issue initilizing the github client.`
if (f.includes('getChangedFiles'))
ret = `There was an issue getting changed files from Github.`
if (f.includes('sortChangedFiles'))
ret = `There was an issue sorting changed files from Github.`
if (f.includes('writeFiles')) ret = `There was an issue writing output files.`
if (f.includes('writeOutput'))
ret = `There was an issue writing output variables.`
return `${ret}\nException: ${error}`
}

View File

@@ -0,0 +1,40 @@
import {setFailed as coreSetFailed} from '@actions/core'
import {getInputs, inferInput} from './InputHelper'
import {writeOutput, writeFiles, sortChangedFiles} from './FilesHelper'
import {getChangedFiles, initClient} from './GithubHelper'
import {errorMessage} from './UtilsHelper'
// figure out if it is a PR or Push
export async function run(): Promise<void> {
try {
// get inputs
const inputs = getInputs()
// parse input
const inferred = inferInput(
inputs.pushBefore,
inputs.pushAfter,
inputs.prNumber
)
// prepare client
const client = initClient(inputs.githubToken)
// get changed files
const changedFilesArray = await getChangedFiles(
client,
inputs.githubRepo,
inferred
)
// sort changed files
const changedFiles = sortChangedFiles(changedFilesArray)
Object.keys(changedFiles).forEach(key => {
// write file output
writeFiles(inputs.fileOutput, key, changedFiles[key])
// write output vars
writeOutput(inputs.output, key, changedFiles[key])
})
} catch (error) {
const pError = JSON.parse(error.message)
coreSetFailed(errorMessage(pError.from, pError))
throw new Error(JSON.stringify(pError))
}
}
/* istanbul ignore next */
if (!(process.env.INPUT_MOCK === 'true')) run()

View File

@@ -0,0 +1,240 @@
import {Env, p, getTestFiles, getTestEvents} from './mocks/env'
let env: Env
describe('Testing FilesHelper.ts...', () => {
describe('...with push event...', () => {
beforeAll(() => {
env = new Env({}, {githubToken: 'TestToken'}, 'push')
})
afterEach(() => {
process.env = {...env.envStart}
jest.resetModules()
env = new Env({}, {}, 'push')
})
/**
* @function sortChangedFiles
*/
describe('...with function sortChangedFiles...', () => {
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])(
'...correctly sorts GithubFile array into ChangedFiles object %i/10 times',
() => {
const {files, stats} = getTestFiles()
const changedFiles = require('../FilesHelper').sortChangedFiles(files)
const coreDebug = require('@actions/core').debug
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(files, null, 2))
)
const retStats = {
files: 0,
added: 0,
removed: 0,
modified: 0,
renamed: 0
} as {
[key: string]: number
}
Object.keys(changedFiles).forEach(key => {
retStats[key] = changedFiles[key].length
})
expect(retStats).toStrictEqual(stats)
}
)
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])(
'...correctly sorts GithubFile array into ChangedFiles object without filenames %i/10 times',
() => {
const {files, stats} = getTestFiles()
const changedFiles = require('../FilesHelper').sortChangedFiles(files)
const coreDebug = require('@actions/core').debug
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(files, null, 2))
)
const retStats = {
files: 0,
added: 0,
removed: 0,
modified: 0,
renamed: 0
} as {
[key: string]: number
}
Object.keys(changedFiles).forEach(key => {
retStats[key] = changedFiles[key].length
})
expect(retStats).toStrictEqual(stats)
}
)
it('...throws an error', () => {
expect(() =>
require('../FilesHelper').sortChangedFiles({
filename: '/test/file.txt',
status: 'noexist'
})
).toThrowError(
JSON.stringify(
{
error: '500/TypeError',
from: 'sortChangedFiles',
message: 'There was an issue sorting files changed.',
payload: JSON.stringify({})
},
null,
2
)
)
})
})
/**
* @function getFormatExt
*/
describe('...with function getFormatExt...', () => {
it.each(getTestEvents(p.getFormatExtInputs, 'push'))(
'...sets %s ext for input "%s" should be "%s"',
(inputName, input, expected) => {
const ext = require('../FilesHelper').getFormatExt(input)
expect(ext).toBe(expected)
}
)
})
/**
* @function formatChangedFiles
*/
describe('...with function formatChangedFiles...', () => {
it.each(
getTestEvents(
p.changedFilesInput('push', ['/test/file', '/test/file2']),
'push'
)
)('... with %o', (format, input, expected) => {
const ext = require('../FilesHelper').formatChangedFiles(format, input)
expect(ext).toBe(expected)
if (format === 'json') expect(ext).toBe(`["${input[0]}","${input[1]}"]`)
else expect(ext).toBe(`${input[0]}${format}${input[1]}`)
})
it.each(getTestEvents(p.changedFilesInput('push'), 'push'))(
'...formats a big list %s',
(inputName, input, expected) => {
const ext = require('../FilesHelper').formatChangedFiles(
inputName,
input
)
expect(ext).toBe(expected)
}
)
})
/**
* @function writeFiles
*/
describe('...with function writeFiles...', () => {
it.each(getTestEvents(p.changedFilesInput('push'), 'push'))(
'...writesFiles %s',
(inputName, input, expected) => {
const coreDebug = require('@actions/core').debug
const fsWriteFilesSync = require('fs').writeFileSync
const format = require('../FilesHelper').getFormatExt(inputName)
require('../FilesHelper').writeFiles(inputName, 'testKey', input)
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(input, null, 2))
)
expect(fsWriteFilesSync).toHaveBeenCalledWith(
`${process.env.HOME}/files_testKey${format}`,
expected,
'utf-8'
)
}
)
it.each(getTestEvents(p.changedFilesInput('push'), 'push'))(
'...writesFiles %s with files key',
(inputName, input, expected) => {
const coreDebug = require('@actions/core').debug
const fsWriteFilesSync = require('fs').writeFileSync
const format = require('../FilesHelper').getFormatExt(inputName)
require('../FilesHelper').writeFiles(inputName, 'files', input)
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(input, null, 2))
)
expect(fsWriteFilesSync).toHaveBeenCalledWith(
`${process.env.HOME}/files${format}`,
expected,
'utf-8'
)
}
)
it('...throws error', () => {
const coreDebug = require('@actions/core').debug
expect(() =>
require('../FilesHelper').writeFiles('error', 'testKey', 'json')
).toThrowError(
new Error(
JSON.stringify(
{
error: '500/TypeError',
from: 'writeFiles',
message: 'There was an issue writing output files.',
payload: JSON.stringify({})
},
null,
2
)
)
)
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(
`Writing output file ${process.env.HOME}/files_testKey.txt with error and files "json"`
)
)
})
})
/**
* @function writeOutput
*/
describe('...with function writeOutput...', () => {
it.each(getTestEvents(p.changedFilesInput('push'), 'push'))(
'...writeOutput %o',
(inputName, input, expected) => {
const coreDebug = require('@actions/core').debug
const coreSetOutput = require('@actions/core').setOutput
require('../FilesHelper').writeOutput(inputName, 'testKey', input)
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(input, null, 2))
)
expect(coreSetOutput).toHaveBeenCalledWith(`files_testKey`, expected)
}
)
it.each(getTestEvents(p.changedFilesInput('push'), 'push'))(
'...writeOutput %o with files key',
(inputName, input, expected) => {
const coreDebug = require('@actions/core').debug
const coreSetOutput = require('@actions/core').setOutput
require('../FilesHelper').writeOutput(inputName, 'files', input)
expect(coreDebug).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(input, null, 2))
)
expect(coreSetOutput).toHaveBeenCalledWith(`files`, expected)
}
)
it('...throws error', () => {
const coreDebug = require('@actions/core').debug
expect(() =>
require('../FilesHelper').writeOutput('error', 'testKey', 'json')
).toThrowError(
new Error(
JSON.stringify(
{
error: '500/TypeError',
from: 'writeOutput',
message: 'There was an issue setting action outputs.',
payload: JSON.stringify({})
},
null,
2
)
)
)
expect(coreDebug).toHaveBeenCalledWith(
'Writing output files_testKey with error and files "json"'
)
})
})
})
})

View File

@@ -0,0 +1,241 @@
import {Env, p, getTestEvents} from './mocks/env'
let env: Env
describe('Testing GithubHelper.ts...', () => {
describe.each(p.testEvents)('...with %s event...', event => {
beforeAll(() => {
env = new Env({}, {githubToken: 'TestToken'}, event)
})
afterEach(() => {
process.env = {...env.envStart}
jest.resetModules()
env = new Env({}, {}, event)
})
/**
* @function initClient
*/
describe('...with function initClientTests...', () => {
it.each(getTestEvents(p.initClientTestInputs, event))(
'...%s',
async (title, input, expected) => {
process.env = {...env.envStart}
env = new Env({}, {}, event)
let gh
if (title.includes('without a token'))
expect(() =>
require('../GithubHelper').initClient(input)
).toThrowError(
new Error(
JSON.stringify(
{
error: '500/Error',
from: 'initClient',
message:
'There was an error creating github client. Please check your token.',
payload: {}
},
null,
2
)
)
)
else {
gh = require('../GithubHelper').initClient(input)
const {GitHub} = require('@actions/github')
expect(GitHub).toHaveBeenCalledTimes(1)
expect(GitHub).toHaveBeenCalledWith(expected)
expect(gh).toEqual(env.octokitMock)
}
}
)
})
/**
* @function getChangedPRFiles
*/
describe('...with function getChangedPRFiles...', () => {
it.each(getTestEvents(p.getChangedPRFilesTestInputs, event))(
'...%s',
async (title, input, expected) => {
if (title.includes('throws an error')) {
expect.assertions(1)
await expect(
require('../GithubHelper').getChangedPRFiles(
env.octokitMock,
input.repo,
input.owner,
input.pullNumber
)
).rejects.toThrowError(new Error(JSON.stringify(expected, null, 2)))
} else {
let files: any[] = []
files = await require('../GithubHelper').getChangedPRFiles(
env.octokitMock,
input.repo,
input.owner,
input.pullNumber
)
expect(files).toStrictEqual(expected)
expect(files.length).toBe(7)
}
}
)
it('...throws errows', async () => {
await expect(
require('../GithubHelper').getChangedPRFiles(
env.octokitMock,
'trilom/file-changes-action',
'error',
'error'
)
).rejects.toThrowError(
new Error(
JSON.stringify(
{
error: '500/Unknown Error:Error',
from: 'getChangedPRFiles',
message:
'There was an error getting change files for repo:trilom/file-changes-action owner:error pr:error',
payload: JSON.stringify({name: 'HttpError', status: '500'})
},
null,
2
)
)
)
await expect(
require('../GithubHelper').getChangedPRFiles(
env.octokitMock,
'trilom/file-changes-action',
'unknown',
'unknown'
)
).rejects.toThrowError(
new Error(
JSON.stringify(
{
error: '500/Unknown Error:',
from: 'getChangedPRFiles',
message:
'There was an error getting change files for repo:trilom/file-changes-action owner:unknown pr:unknown',
payload: ''
},
null,
2
)
)
)
})
})
/**
* @function getChangedPushFiles
*/
describe('...with function getChangedPushFiles...', () => {
it.each(getTestEvents(p.getChangedPushFilesTestInputs, event))(
'...%s',
async (title, input, expected) => {
if (title.includes('throws an error')) {
expect.assertions(1)
await expect(
require('../GithubHelper').getChangedPushFiles(
env.octokitMock,
input.repo,
input.owner,
input.before,
input.after
)
).rejects.toThrowError(new Error(JSON.stringify(expected, null, 2)))
} else {
let files: any[] = []
files = await require('../GithubHelper').getChangedPushFiles(
env.octokitMock,
input.repo,
input.owner,
input.before,
input.after
)
expect(files).toStrictEqual(expected)
expect(files.length).toBe(7)
}
}
)
it('...throws errows', async () => {
await expect(
require('../GithubHelper').getChangedPushFiles(
env.octokitMock,
'trilom/file-changes-action',
'error',
'error',
'error'
)
).rejects.toThrowError(
new Error(
JSON.stringify(
{
error: '500/Unknown Error:Error',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:trilom/file-changes-action owner:error base:error head:error',
payload: JSON.stringify({name: 'HttpError', status: '500'})
},
null,
2
)
)
)
await expect(
require('../GithubHelper').getChangedPushFiles(
env.octokitMock,
'trilom/file-changes-action',
'unknown',
'unknown',
'unknown'
)
).rejects.toThrowError(
new Error(
JSON.stringify(
{
error: '500/Unknown Error:',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:trilom/file-changes-action owner:unknown base:unknown head:unknown',
payload: ''
},
null,
2
)
)
)
})
})
/**
* @function getChangedFiles
*/
describe('...with function getChangedFiles...', () => {
it.each(getTestEvents(p.getChangedFilesTestInputs, event))(
'...%s',
async (title, input, expected) => {
if (title.includes('throws an error')) {
expect.assertions(1)
await expect(
require('../GithubHelper').getChangedFiles(
env.octokitMock,
input.repo,
{...input}
)
).rejects.toThrowError(new Error(JSON.stringify(expected, null, 2)))
} else {
let files: any[] = []
files = await require('../GithubHelper').getChangedFiles(
env.octokitMock,
input.repo,
{...input}
)
expect(files).toStrictEqual(expected)
expect(files.length).toBe(7)
}
}
)
})
})
})

View File

@@ -0,0 +1,222 @@
import {Env, eventName, getTestEvents, p} from './mocks/env'
let env: Env
describe('Testing InputHelper.ts...', () => {
describe.each(p.testEvents)('...with %s event...', event => {
beforeAll(() => {
env = new Env({}, {githubToken: 'TestToken'}, event)
})
afterEach(() => {
process.env = {...env.envStart}
jest.resetModules()
env = new Env({}, {}, event)
})
/**
* @function getInputs
*/
describe('...with function getInputs...', () => {
it('...sets correct default input parameters.', () => {
const {payload, issue, eventName: contextEventName} = env.context
const {
prNumber,
pushAfter,
pushBefore,
githubToken,
githubRepo,
output,
fileOutput,
event: inputEventName
} = require('../InputHelper').getInputs()
const {getInput} = require('@actions/core')
if (event.includes('push')) {
expect(prNumber).toBe(NaN)
expect(pushAfter).toBe(payload.after)
expect(pushBefore).toBe(payload.before)
}
if (event.includes('pull_request') || event.includes('issue_comment')) {
expect(prNumber).toBe(issue.number)
if (event === 'pull_request_synchronize') {
expect(pushAfter).toBe(payload.after)
expect(pushBefore).toBe(payload.before)
} else {
expect(pushAfter).toBeFalsy()
expect(pushBefore).toBeFalsy()
}
}
expect(githubToken).toBe(process.env.INPUT_GITHUBTOKEN)
expect(githubRepo).toBe(process.env.GITHUB_REPOSITORY)
expect(output).toBe(' ')
expect(fileOutput).toBe(' ')
expect(inputEventName).toBe(contextEventName)
expect(getInput).toHaveBeenCalled()
})
it('...throws error with no token (undefined) process.env["GITHUB_TOKEN"] or (undefined) input githubToken', () => {
delete process.env.GITHUB_TOKEN
delete process.env.INPUT_GITHUBTOKEN
const {getInput} = require('@actions/core')
expect(() => {
require('../InputHelper').getInputs()
}).toThrowError()
expect(getInput).toHaveBeenCalledTimes(1)
})
it('...throws error with empty string ("") process.env["GITHUB_TOKEN"] or empty string ("") input githubToken', () => {
env.updateInput({githubToken: ''})
process.env.GITHUB_TOKEN = ''
const {getInput} = require('@actions/core')
expect(() => {
require('../InputHelper').getInputs()
}).toThrowError()
expect(getInput).toHaveBeenCalledTimes(1)
})
it.each(getTestEvents(p.inputTestInputs, event))(
'...sets %s input "%s" should be %p',
(inputName, input, expected) => {
env.updateInput({[inputName]: input})
const {payload, issue, eventName: contextEventName} = env.context
const {
prNumber,
pushAfter,
pushBefore,
githubToken,
githubRepo,
output,
fileOutput,
event: inputEventName
} = require('../InputHelper').getInputs()
const {getInput} = require('@actions/core')
if (event.includes('push')) {
expect(prNumber).toBe(inputName === 'prNumber' ? expected : NaN)
expect(pushAfter).toBe(
inputName === 'pushAfter' ? expected : payload.after
)
expect(pushBefore).toBe(
inputName === 'pushBefore' ? expected : payload.before
)
}
if (
event.includes('pull_request') ||
event.includes('issue_comment')
) {
expect(prNumber).toBe(
inputName === 'prNumber' ? expected : issue.number
)
if (event === 'pull_request_synchronize') {
expect(pushAfter).toBe(
inputName === 'pushAfter' ? expected : payload.after
)
expect(pushBefore).toBe(
inputName === 'pushBefore' ? expected : payload.before
)
} else {
expect(pushAfter).toBe(
inputName === 'pushAfter' ? expected : false
)
expect(pushBefore).toBe(
inputName === 'pushBefore' ? expected : false
)
}
}
expect(githubToken).toBe(
inputName === 'githubToken' ? expected : 'EnvDefaultToken'
)
expect(githubRepo).toBe(
inputName === 'githubRepo'
? expected
: process.env.GITHUB_REPOSITORY
)
expect(output).toBe(inputName === 'output' ? expected : ' ')
expect(fileOutput).toBe(inputName === 'fileOutput' ? expected : ' ')
expect(inputEventName).toBe(contextEventName)
expect(getInput).toBeCalled()
}
)
})
/**
* @function inferInput
*/
describe('...with function inferInput...', () => {
it.each(getTestEvents(p.inferTestInputs, event))(
'...%s',
(title, input, expected) => {
const {error} = require('@actions/core')
const {warning} = require('@actions/core')
if (title.includes('ERROR with no')) {
expect(() => {
require('../InputHelper').inferInput(
input.before,
input.after,
input.pr
)
}).toThrowError(
new Error(
JSON.stringify(
{
error: '500/inferInput Error',
from: 'inferInput',
message: `Received event from ${eventName(
event
)}, but received no inputs. {event_name:${eventName(
event
)}, pr: NaN, before:, after:}`,
payload: ''
},
null,
2
)
)
)
} else if (title.includes('ERROR with single')) {
expect(() => {
require('../InputHelper').inferInput(
input.before,
input.after,
input.pr
)
}).toThrowError(
new Error(
JSON.stringify(
{
error: '500/inferInput Error',
from: 'inferInput',
message: `Received event from ${eventName(
event
)}, but only received a before(${input.before}) or after(${
input.after
}).\n I need both of these if you want to use a Push event.`,
payload: ''
},
null,
2
)
)
)
} else {
const data = require('../InputHelper').inferInput(
input.before,
input.after,
input.pr
)
Object.keys(data).forEach(key =>
expect(data[key]).toBe(expected[key])
)
expect(error).not.toHaveBeenCalled()
}
if (title.includes('WARN weird'))
expect(warning).toHaveBeenCalledWith(
expect.stringContaining(
`received a before(${input.before}) or after(${input.after}) value.`
)
)
if (title.includes('WARN all'))
expect(warning).toHaveBeenCalledWith(
expect.stringContaining(
`but received a before(${input.before}), after(${input.after}), and PR(${input.pr}).`
)
)
else expect(error).not.toHaveBeenCalled()
}
)
})
})
})

View File

@@ -0,0 +1,67 @@
import {Env, p, getTestEvents} from './mocks/env'
let env: Env
describe('Testing UtilsHelper.ts...', () => {
describe('...with push event...', () => {
beforeAll(() => {
env = new Env({}, {githubToken: 'TestToken'}, 'push')
})
afterEach(() => {
process.env = {...env.envStart}
jest.resetModules()
env = new Env({}, {}, 'push')
})
/**
* @function getErrorString
*/
describe('...with function getErrorString...', () => {
it('...can throw an error', () => {
const error = require('../UtilsHelper').getErrorString()
expect(JSON.stringify(JSON.parse(error))).toBe(
JSON.stringify({error: '500/undefined', payload: ''})
)
})
it('...can throw an error for my error', () => {
const {setFailed, error: coreError} = require('@actions/core')
const obj = {a: {}}
obj.a = {b: obj}
expect(() =>
require('../UtilsHelper').getErrorString(
'test',
200,
'test',
'test',
obj
)
).toThrowError(
JSON.stringify({
name: '500/undefined',
message: 'Error throwing error.'
})
)
// expect(JSON.stringify(JSON.parse(error))).toBe(JSON.stringify({error:'500/undefined', payload:''}))
expect(setFailed).toBeCalledWith(
expect.stringContaining('Error throwing error.')
)
expect(coreError).toBeCalledWith(
expect.stringContaining('Error throwing error.')
)
})
})
/**
* @function errorMessage
*/
describe('...with function errorMessage...', () => {
it.each(getTestEvents(p.errorMessageInputs, 'push'))(
'...for function %s',
(f, e, expected) => {
const error = require('../UtilsHelper').errorMessage(f, e)
expect(error).toBe(
`${expected}\nException: ${JSON.stringify(e, null, 2)}`
)
}
)
})
})
})

View File

@@ -0,0 +1,129 @@
import {existsSync, mkdirSync, readFileSync, rmdirSync, unlinkSync} from 'fs'
import {resolve as _resolve} from 'path'
import {
eventName as formatEventName,
formatInput,
getTestEvents,
p
} from './mocks/env'
// debugger
const pEnv: {[key: string]: string | undefined} = {...process.env}
let processStdoutMock: jest.SpyInstance
let consoleLogMock: jest.SpyInstance
let output = ''
describe.each(p.testEvents)('Testing main.ts with %s event...', event => {
/**
* @function run
*/
describe.each(getTestEvents(p.mainInputs, event))(
'...function run with %s event inputs non mocked...',
(eventName, eventInput, eventExpected) => {
describe.each(getTestEvents(p.getFormatExtInputs, 'push'))(
'...with output %s...',
(outputName, outputInput, outputExpected) => {
describe.each(getTestEvents(p.getFormatExtInputs, 'push'))(
'...with fileOutput %s...',
(fileOutputName, fileOutputInput, fileOutputExpected) => {
beforeEach(() => {
consoleLogMock = jest
.spyOn(console, 'log')
.mockImplementation((message: string) => {
output += ` ${message}`
})
processStdoutMock = jest
.spyOn(process.stdout, 'write')
.mockImplementation(
(
command: string | Uint8Array,
encoding?: string,
cb?: () => void
) => {
output += ` ${command}`
return false
}
)
mkdirSync(
_resolve(
__dirname,
`outputs/${event}/${eventName}/o_${outputName}f_${fileOutputName}`
),
{recursive: true}
)
process.env = {
HOME: _resolve(
__dirname,
`outputs/${event}/${eventName}/o_${outputName}f_${fileOutputName}`
),
GITHUB_EVENT_NAME: formatEventName(event),
GITHUB_EVENT_PATH: _resolve(
__dirname,
`mocks/env/events/${event}.json`
),
...formatInput({
githubRepo: 'trilom/file-changes-action',
githubToken: process.env.GITHUB_TOKEN || '',
output: outputInput,
fileOutput: fileOutputInput,
...eventInput
})
}
})
afterEach(() => {
process.env = {...pEnv}
output = ''
jest.restoreAllMocks()
})
it('...no-mock', async () => {
await expect(require('../main').run()).resolves.toBe(undefined)
const counts = {
files: 73,
files_added: 52,
files_modified: 13,
files_removed: 8
} as {[key: string]: number}
Object.keys(counts).forEach(async key => {
expect(output).toContain(`::set-output name=${key}`)
expect(
existsSync(
_resolve(
__dirname,
`outputs/${event}/${eventName}/o_${outputName}f_${fileOutputName}/${key}${fileOutputExpected}`
)
)
).toBeTruthy()
if (fileOutputExpected === '.json') {
expect(
JSON.parse(
readFileSync(
_resolve(
__dirname,
`outputs/${event}/${eventName}/o_${outputName}f_${fileOutputName}/${key}${fileOutputExpected}`
),
'utf8'
)
)
).toHaveLength(counts[key])
} else {
expect(
readFileSync(
_resolve(
__dirname,
`outputs/${event}/${eventName}/o_${outputName}f_${fileOutputName}/${key}${fileOutputExpected}`
),
'utf8'
).split(fileOutputInput)
).toHaveLength(counts[key])
}
})
}, 10000)
}
)
}
)
}
)
})

View File

@@ -0,0 +1,89 @@
import {Env, getTestEvents, getTestFiles, p} from './mocks/env'
let env: Env
describe('Testing main.ts...', () => {
describe.each(p.testEvents)('...with %s event...', event => {
/**
* @function run
*/
describe('...with function run...', () => {
describe.each(getTestEvents(p.getFormatExtInputs, 'push'))(
'...with fileOutput %s...',
(fileOutputName, fileOutputInput, fileOutputExpected) => {
describe.each(getTestEvents(p.getFormatExtInputs, 'push'))(
'...with output %o...',
(outputName, outputInput, outputExpected) => {
describe.each(getTestEvents(p.mainInputs, event))(
'...with %s event inputs mocked...',
(eventName, eventInput, eventExpected) => {
beforeEach(() => {
env = new Env(
{},
{
githubRepo: 'trilom/file-changes-action',
githubToken: 'TestToken',
output: outputInput,
fileOutput: fileOutputInput,
...eventInput,
mock: 'true'
},
event
)
})
afterEach(() => {
process.env = env.envStart
jest.resetModules()
jest.unmock('@actions/core')
jest.unmock('@actions/github')
jest.unmock('../InputHelper')
jest.unmock('../FilesHelper')
jest.unmock('../GithubHelper')
})
it('...mocked', async () => {
const githubHelper = require('../GithubHelper')
const filesHelper = require('../FilesHelper')
githubHelper.getChangedFiles = jest.fn(
() => getTestFiles().files
)
filesHelper.writeOutput = jest.fn(() => {})
filesHelper.writeFiles = jest.fn(() => {})
await expect(require('../main').run()).resolves.toBe(
undefined
)
expect(githubHelper.getChangedFiles).toBeCalled()
expect(filesHelper.writeOutput).toBeCalled()
expect(filesHelper.writeFiles).toBeCalled()
})
it.each(getTestEvents(p.mainErrorInputs, 'push'))(
'...throws error for mocked function %s...',
async (f, e, expected) => {
const inputHelper = require('../InputHelper')
let thrown = false
inputHelper.getInputs = jest.fn(() => {
thrown = true
throw new Error(e)
})
await expect(
require('../main').run()
).rejects.toThrowError(
new Error(
JSON.stringify({
name: 'Error',
message: 'Error',
from: f
})
)
)
expect(inputHelper.getInputs).toHaveBeenCalledTimes(1)
}
)
}
)
}
)
}
)
})
})
})

View File

@@ -0,0 +1,55 @@
import {mock} from '.'
const core = mock()
describe('Testing CoreMock object...', () => {
beforeAll(() => jest.restoreAllMocks())
it('...CoreMock is a mock', () => {
expect(jest.isMockFunction(core.getInput)).toBe(true)
expect(jest.isMockFunction(core.setFailed)).toBe(true)
expect(jest.isMockFunction(core.setOutput)).toBe(true)
expect(jest.isMockFunction(core.debug)).toBe(true)
expect(jest.isMockFunction(core.warning)).toBe(true)
expect(jest.isMockFunction(core.info)).toBe(true)
expect(jest.isMockFunction(core.error)).toBe(true)
})
it('...CoreMock mocks core', () => {
const realCore = require('@actions/core')
expect(core).toMatchObject(realCore)
})
it('...CoreMock mocks setFailed', () => {
core.setFailed('Test Message')
expect(core.error).toBeCalledWith('Test Message')
expect(core.setFailed).toBeCalledWith('Test Message')
})
it('...CoreMock mocks setOutput', () => {
core.setOutput('TestName', 'TestValue')
expect(core.setOutput).toBeCalledWith('TestName', 'TestValue')
})
it('...CoreMock mocks setOutput error', () => {
expect(() => core.setOutput('ERROROUTPUT', 'TestValue')).toThrowError(
new Error(JSON.stringify({name: 'CoreError', status: '500'}))
)
})
it('...CoreMock mocks getInput', () => {
process.env.INPUT_TEST = 'TESTINPUT'
const input = core.getInput('TEST')
expect(input).toBe('TESTINPUT')
})
it('...CoreMock mocks debug', () => {
core.debug('Test Message')
expect(core.debug).toBeCalledWith('Test Message')
})
it('...CoreMock mocks warning', () => {
core.warning('Test Message')
expect(core.warning).toBeCalledWith('Test Message')
})
it('...CoreMock mocks info', () => {
core.info('Test Message')
expect(core.info).toBeCalledWith('Test Message')
})
it('...CoreMock mocks error', () => {
core.error('Test Message')
expect(core.error).toBeCalledWith('Test Message')
})
})

View File

@@ -0,0 +1,34 @@
import {CoreMock} from 'typings/CoreMock'
const coreMock: CoreMock = {
setFailed: jest.fn(message => {
coreMock.error(message)
// console.error(`setFailed triggered`)
}),
setOutput: jest.fn((name, value) => {
if (name === 'ERROROUTPUT')
throw new Error(JSON.stringify({name: 'CoreError', status: '500'}))
// console.log(`setOutputName: ${name} value: ${value}`)
}),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getInput: jest.fn((name, options) => {
return process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`]
}),
debug: jest.fn(message => {
// console.debug(`core.debug triggered: ${message}`)
}),
warning: jest.fn(message => {
// console.warn(`core.warning triggered: ${message}`)
}),
info: jest.fn(message => {
// console.info(`core.info triggered: ${message}`)
}),
error: jest.fn(message => {
// console.error(`core.error triggered: ${message}`)
})
}
export function mock(): CoreMock {
jest.mock('@actions/core', () => coreMock)
return coreMock
}

View File

@@ -0,0 +1,23 @@
import {mock} from '.'
const fs = mock()
describe('Testing FsMock object...', () => {
beforeAll(() => jest.restoreAllMocks())
it('...FsMock is a mock', () => {
expect(jest.isMockFunction(fs.writeFileSync)).toBe(true)
})
it('...FsMock mocks fs', () => {
const realFs = require('fs')
expect(fs).toMatchObject(realFs)
})
it('...FsMock mocks writeFileSync', () => {
fs.writeFileSync('a', 'b', 'c')
expect(fs.writeFileSync).toBeCalledWith('a', 'b', 'c')
})
it('...FsMock mocks an error', async () => {
expect(() => fs.writeFileSync('error', 'b', 'c')).toThrowError(
new Error(JSON.stringify({name: 'PathError', status: '500'}))
)
})
})

View File

@@ -0,0 +1,14 @@
import {FsMock} from 'typings/FsMock'
const fsMock = {
writeFileSync: jest.fn((path, data, options) => {
if (path === 'error')
throw new Error(JSON.stringify({name: 'PathError', status: '500'}))
// console.log(`fs.writeFileSync triggered with path: ${path} data: ${data} options: ${options}`)
})
}
export function mock(): FsMock {
jest.mock('fs', () => fsMock)
return fsMock
}

View File

@@ -0,0 +1,29 @@
import {Context} from '@actions/github/lib/context'
import {mock} from '.'
import {octokitMock} from '../octokit'
const github = mock()
describe('Testing GitHubMock object ...', () => {
beforeAll(() => jest.restoreAllMocks())
it('...GitHubMock is a mock', () => {
expect(jest.isMockFunction(github.github.GitHub)).toBe(true)
expect(github.context).toMatchObject(new Context())
})
it('...GitHubMock mocks GitHub', () => {
const {GitHub} = require('@actions/github')
const mockGitHub = GitHub('test')
expect(mockGitHub).toMatchObject(octokitMock)
})
it('...GitHubMock mocks unauthorized GitHub', () => {
const GitHub = mock()
expect(jest.isMockFunction(GitHub.github.GitHub)).toBe(true)
})
it('...GitHubMock mocks authorizing GitHub', () => {
const GitHub = mock()
const octokit = GitHub.github.GitHub('token')
expect(jest.isMockFunction(GitHub.github.GitHub)).toBe(true)
expect(GitHub.github.GitHub).toBeCalledWith('token')
expect(octokit).toMatchObject(octokitMock)
})
})

View File

@@ -0,0 +1,29 @@
import {Context} from '@actions/github/lib/context'
import {GitHubMock} from 'typings/GitHubMock'
import {OctokitMock} from 'typings/OctokitMock'
import {octokitMock} from '../octokit'
function getGitHubMock(context: Context): GitHubMock {
return {
GitHub: jest.fn(token => {
// console.log(`I am authorizing GitHub with token: ${token}`)
if (!token)
throw new Error(
JSON.stringify({name: 'GithubInitError', status: '500'})
)
return octokitMock
}),
context
}
}
export function mock(): {
github: GitHubMock
octokit: OctokitMock
context: Context
} {
const context = new Context()
const github = getGitHubMock(context)
jest.mock('@actions/github', () => github)
return {github, octokit: octokitMock, context}
}

View File

@@ -0,0 +1,48 @@
import {EndpointOptions} from '@octokit/types'
import {fn as merge} from './merge'
import {
OctokitPullsListFilesEndpointMergeRequest,
OctokitPullsListFilesEndpointMergeResponse,
OctokitReposCompareCommitsEndpointMergeRequest,
OctokitReposCompareCommitsEndpointMergeResponse
} from '../payloads'
describe('Testing Octokit object...', () => {
beforeAll(() => {
jest.restoreAllMocks()
})
it('...endpoint.merge returns 500 error with pull_number "error"', () => {
expect(() => {
merge(({pull_number: 'error'} as unknown) as EndpointOptions)
}).toThrowError(
new Error(JSON.stringify({name: 'HttpError', status: '500'}))
)
})
it('...endpoint.merge returns 500 error with base "error"', () => {
expect(() => {
merge(({base: 'error'} as unknown) as EndpointOptions)
}).toThrowError(
new Error(JSON.stringify({name: 'HttpError', status: '500'}))
)
})
it('...endpoint.merge returns empty object', async () => {
const request = OctokitPullsListFilesEndpointMergeRequest
const data = merge({...request, pull_number: NaN})
expect(data).toStrictEqual({
...OctokitPullsListFilesEndpointMergeResponse,
...{pull_number: NaN, base: '', head: ''}
})
})
it('...endpoint.merge for pull request', () => {
const request = OctokitPullsListFilesEndpointMergeRequest
const response = OctokitPullsListFilesEndpointMergeResponse
const data = merge(request)
expect(data).toStrictEqual(response)
})
it('...endpoint.merge for push', () => {
const request = OctokitReposCompareCommitsEndpointMergeRequest
const response = OctokitReposCompareCommitsEndpointMergeResponse
const data = merge(request)
expect(data).toStrictEqual(response)
})
})

View File

@@ -0,0 +1,39 @@
// Import Request and Response Objects
import {EndpointOptions, RequestOptions} from '@octokit/types'
import {
// OctokitReposCompareCommitsEndpointMergeRequest,
OctokitReposCompareCommitsEndpointMergeResponse,
// OctokitPullsListFilesEndpointMergeRequest,
OctokitPullsListFilesEndpointMergeResponse
} from '../payloads'
// Form and export Response Objects
export {OctokitReposCompareCommitsEndpointMergeResponse as pushResponse}
export {OctokitPullsListFilesEndpointMergeResponse as prResponse}
// Export mock function
export const fn = jest.fn((data: EndpointOptions, response?: number) => {
if (data.base === 'error' || data.pull_number === 'error') {
throw new Error(JSON.stringify({name: 'HttpError', status: '500'}))
}
if (data.base === 'unknown' || data.pull_number === 'unknown') {
throw JSON.stringify({idk: 'error', message: 'test'})
}
if (
(!data.base && !data.head && Number.isNaN(data.pull_number)) ||
(!data.base && data.head) ||
(data.base && !data.head)
)
return {
...OctokitPullsListFilesEndpointMergeResponse,
...{pull_number: NaN, base: '', head: ''}
} as RequestOptions
if (data.pull_number) {
return {
...OctokitPullsListFilesEndpointMergeResponse,
...data
} as RequestOptions
}
return {
...OctokitReposCompareCommitsEndpointMergeResponse,
...data
} as RequestOptions
})

View File

@@ -0,0 +1,11 @@
import {octokitMock} from '.'
describe('Testing Octokit object ...', () => {
beforeAll(() => jest.restoreAllMocks())
it('...Octokit is a mock', () => {
expect(octokitMock).toHaveProperty('paginate')
expect(octokitMock).toHaveProperty('pulls')
expect(octokitMock).toHaveProperty('repos')
expect(octokitMock).not.toHaveProperty('actions')
})
})

View File

@@ -0,0 +1,32 @@
import {EndpointOptions, OctokitResponse} from '@octokit/types'
import {OctokitMock} from 'typings/OctokitMock'
// mock endpoints
import {fn as endpointMerge} from './endpoint/merge'
import {fn as paginate} from './paginate'
import {fn as listFiles} from './pulls/listFiles'
import {fn as compareCommits} from './repos/compareCommits'
// new object
export const octokitMock: OctokitMock = {
paginate: (
data: EndpointOptions,
cb?: (response: OctokitResponse<any>) => Promise<any[]>
) => paginate(data, cb),
pulls: {
listFiles: Object.assign((data: EndpointOptions) => listFiles(data), {
endpoint: {
merge: (data: EndpointOptions) => endpointMerge(data)
}
})
},
repos: {
compareCommits: Object.assign(
(data: EndpointOptions) => compareCommits(data),
{
endpoint: {
merge: (data: EndpointOptions) => endpointMerge(data)
}
}
)
}
}

View File

@@ -0,0 +1,55 @@
import {fn as paginate} from './paginate'
import {
OctokitPaginatePrRequest,
OctokitPaginatePrResponse,
OctokitPaginatePushRequest,
OctokitPaginatePushResponse
} from './payloads'
describe('Testing Octokit object...', () => {
beforeAll(() => {
jest.restoreAllMocks()
})
it('...paginate(request) throws a 404', async () => {
const request = OctokitPaginatePrRequest
await expect(
paginate({...request, pull_number: NaN})
).rejects.toMatchObject({name: 'HttpError', status: '404'})
})
it('...paginate(request) for pull request', () => {
const request = OctokitPaginatePrRequest
const response = OctokitPaginatePrResponse
return paginate(request).then(data => {
expect(data).toStrictEqual(response)
return expect(data.length).toBe(7)
})
})
it('...paginate(request, callback) for pull request', () => {
const request = OctokitPaginatePrRequest
const response = OctokitPaginatePrResponse
return paginate(request, res => {
return res.data
}).then(data => {
expect(data).toStrictEqual(response)
return expect(data.length).toBe(7)
})
})
it('...paginate(request) for push', async () => {
const request = OctokitPaginatePushRequest
const response = OctokitPaginatePushResponse
expect.assertions(1)
const files = await paginate(request).then(data => {
return data.map(commit => commit.files)
})
expect(files).toStrictEqual([response])
})
it('...paginate(request, callback) for push', async () => {
const request = OctokitPaginatePushRequest
const response = OctokitPaginatePushResponse
const files = await paginate(request, res => {
return res.data.files
})
expect(files).toStrictEqual(response)
expect(files.length).toBe(7)
})
})

View File

@@ -0,0 +1,39 @@
// Import Request and Response Objects
import {EndpointOptions, OctokitResponse} from '@octokit/types'
import {
// OctokitPaginatePrRequest,
OctokitPaginatePrResponse,
// OctokitPaginatePushRequest,
OctokitPaginatePushResponse
} from './payloads'
// Form and export Response Objects
export const prResponse = OctokitPaginatePrResponse
export const pushResponse = {files: OctokitPaginatePushResponse}
// Export mock function
export const fn = jest.fn(
(
data: EndpointOptions,
cb?: (response: OctokitResponse<any>) => Promise<any[]>
) => {
if (
data.owner !== 'trilom' ||
data.repo !== 'file-changes-action' ||
(data.base && !data.head) ||
(!data.base && data.head) ||
// the github api doesn't seem to return an error
// eslint-disable-next-line prefer-promise-reject-errors
(!data.base && !data.head && !data.pull_number)
)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject({name: 'HttpError', status: '404'})
if (data.pull_number) {
if (cb)
return Promise.resolve(cb({data: prResponse} as OctokitResponse<any>))
return Promise.resolve(prResponse)
}
if (cb)
return Promise.resolve(cb({data: pushResponse} as OctokitResponse<any>))
return Promise.resolve([pushResponse])
}
)

View File

@@ -0,0 +1,178 @@
import {EndpointOptions} from '@octokit/types'
/**
* FILES
*/
const listFilesResponse: any[] = [
{
filename: '.github/CONTRIBUTING.md',
status: 'added'
},
{
filename: '.github/craneEventLambda.md',
status: 'added'
},
{
filename: '.github/readme.md',
status: 'added'
},
{
filename: 'functions/twitch-sadako/webhookSubscribeLambda/handler.py',
status: 'added'
},
{
filename: 'functions/twitch-sadako/getCraneStatsLambda/handler.py',
status: 'removed'
},
{
filename: '.github/ISSUE_TEMPLATE/bug_report.md',
status: 'modified'
},
{
filename:
'functions/twitch-sadako/webhookSubscribeLambda/test/webhookSubscribeLambda.json',
status: 'renamed'
}
]
const pushRequest: any = {
owner: 'trilom',
repo: 'file-changes-action',
base: '01a956ad7dbd39773299d421b402535cef6ab1f3',
head: '513ca39ff3756e5b510ad752edaba6a0aeb2efac'
}
const prRequest: any = {
owner: 'trilom',
repo: 'file-changes-action',
pull_number: 79
}
const pushEndpointOptions: EndpointOptions = {
method: 'GET',
baseUrl: 'https://api.github.com',
url: '/repos/:owner/:repo/compare/:base...:head',
owner: 'trilom',
repo: 'file-changes-action',
base: '01a956ad7dbd39773299d421b402535cef6ab1f3',
head: '513ca39ff3756e5b510ad752edaba6a0aeb2efac'
}
const prEndpointOptions: EndpointOptions = {
method: 'GET',
baseUrl: 'https://api.github.com',
url: '/repos/:owner/:repo/pulls/:pull_number/files',
owner: 'trilom',
repo: 'file-changes-action',
pull_number: 79
}
export const normalFileArray: string[] = [
'.github/actions/deploy_infrastructure/deploy',
'.github/actions/deploy_infrastructure/deploy_delete_commands.json',
'.github/actions/deploy_infrastructure/deploy_deploy_commands.json',
'.github/actions/deploy_infrastructure/deploy_validate_commands.json',
'.github/actions/deploy_infrastructure/prefix_deploy_commands.json',
'.github/actions/deploy_infrastructure/suffix_deploy_commands.json',
'.python-version',
'cloudformation/.python-version',
'cloudformation/mappings/mappings.twitch.yml',
'cloudformation/order/twitch.yml',
'cloudformation/templates/twitch-sadako.yml',
'cloudformation/templates/twitch-secrets.yml',
'functions/twitch-sadako/Makefile',
'functions/twitch-sadako/craneEventLambda/handler.py',
'functions/twitch-sadako/craneEventLambda/requirements.txt',
'functions/twitch-sadako/craneEventLambda/test/craneEventLambda.json',
'functions/twitch-sadako/followEventLambda/handler.py',
'functions/twitch-sadako/followEventLambda/requirements.txt',
'functions/twitch-sadako/followEventLambda/test/followEventLambda_get.json',
'functions/twitch-sadako/followEventLambda/test/followEventLambda_post.json',
'functions/twitch-sadako/getCraneStatsLambda/handler.py',
'functions/twitch-sadako/getCraneStatsLambda/requirements.txt',
'functions/twitch-sadako/getCraneStatsLambda/test/getCraneStatsLambda.json',
'functions/twitch-sadako/getTokenLambda/handler.py',
'functions/twitch-sadako/getTokenLambda/requirements.txt',
'functions/twitch-sadako/getTokenLambda/test/getTokenLambda.json',
'functions/twitch-sadako/slackNotifyLambda/handler.py',
'functions/twitch-sadako/slackNotifyLambda/requirements.txt',
'functions/twitch-sadako/slackNotifyLambda/test/slackNotifyLambda.json',
'functions/twitch-sadako/webhookSubscribeCronLambda/handler.py',
'functions/twitch-sadako/webhookSubscribeCronLambda/requirements.txt',
'functions/twitch-sadako/webhookSubscribeCronLambda/test/webhookSubscribeCronLambda.json',
'functions/twitch-sadako/webhookSubscribeLambda/handler.py',
'functions/twitch-sadako/webhookSubscribeLambda/requirements.txt',
'functions/twitch-sadako/webhookSubscribeLambda/test/webhookSubscribeLambda.json',
'functions/twitch-sadako/webhookSubscribeLambda/test/webhookSubscribeLambda_post.json',
'.github/ISSUE_TEMPLATE/bug_report.md',
'.github/ISSUE_TEMPLATE/feature_request.md',
'.github/workflows/nodejs.yml',
'.gitignore',
'.npmignore',
'.vscode/launch.json',
'README-template.md',
'README.md',
'jest.config.js',
'package-lock.json',
'package.json',
'src/boolean-serializer.ts',
'src/date-serializer.ts',
'src/deserialize.ts',
'src/index.ts',
'src/model.ts',
'src/serialize-set.ts',
'src/serialize.ts',
'src/utilities.ts',
'src/validation.ts',
'test/unit/_ramda.sp.ts',
'test/unit/benchmark.spec.ts',
'test/unit/boolean-serializer.spec.ts',
'test/unit/data/generators.ts',
'test/unit/date-serializer.spec.ts',
'test/unit/deserialize.spec.ts',
'test/unit/sandbox.spec.ts',
'test/unit/sap/input.ts',
'test/unit/sap/json-object-mapper.ts',
'test/unit/sap/output.ts',
'test/unit/sap/serializers.ts',
'test/unit/serialize.spec.ts',
'test/unit/setup.ts',
'test/unit/test-typings.ts',
'test/unit/test-typings2.ts',
'tsconfig.build.json',
'tsconfig.json'
]
export const weirdFileArray: string[] = [
'.cfnlintrc',
".github/actions/deploy_infrastruc1234'ture/deploy.sh",
'.github/actions/make_commands/&&&index.js',
'.github/actions/make_comdf&*(@mands/test/files_added.json',
'.github/actions/make_c``ommands/test/files_removed.json',
'.github/actions/make commands/test/files_modified.json',
'.pre-commit-config.yaml',
'cloudformation/Makefile',
'cloudformation/mappings/mappings.awsaccount.yml'
]
/**
* REQUESTS
*/
// Octokit.pulls.listFiles
export {prRequest as OctokitPullsListFilesRequest}
// Octokit.pulls.listFiles.endpoint.merge
export {prRequest as OctokitPullsListFilesEndpointMergeRequest}
// Octokit.repos.compareCommits
export {pushRequest as OctokitReposCompareCommitsRequest}
// Octokit.repos.compareCommits.endpoint.merge
export {pushRequest as OctokitReposCompareCommitsEndpointMergeRequest}
// Octokit.paginate
export {prEndpointOptions as OctokitPaginatePrRequest} // pr
export {pushEndpointOptions as OctokitPaginatePushRequest} // push
/**
* RESPONSES
*/
// Octokit.pulls.listFiles
export {listFilesResponse as OctokitPullsListFilesResponse}
// Octokit.pulls.listFiles.endpoint.merge
export {prEndpointOptions as OctokitPullsListFilesEndpointMergeResponse}
// Octokit.repos.compareCommits
export {listFilesResponse as OctokitReposCompareCommitsResponse}
// Octokit.repos.compareCommits.endpoint.merge
export {pushEndpointOptions as OctokitReposCompareCommitsEndpointMergeResponse}
// Octokit.paginate
export {listFilesResponse as OctokitPaginatePrResponse} // pr
export {listFilesResponse as OctokitPaginatePushResponse} // push

View File

@@ -0,0 +1,19 @@
import {fn as listFiles} from './listFiles'
import {
OctokitPullsListFilesRequest,
OctokitPullsListFilesResponse
} from '../payloads'
describe('Testing Octokit object...', () => {
beforeAll(() => {
jest.restoreAllMocks()
})
it('...pulls.listFiles(request) for pull request', () => {
const request = OctokitPullsListFilesRequest
const response = OctokitPullsListFilesResponse
return listFiles(request).then(data => {
expect(data.data).toBe(response)
return expect(data.data.length).toBe(7)
})
})
})

View File

@@ -0,0 +1,14 @@
// Import Request and Response Objects
import {EndpointOptions, OctokitResponse} from '@octokit/types'
import {
// OctokitPullsListFilesRequest,
OctokitPullsListFilesResponse
} from '../payloads'
// Form and export Response Objects
export {OctokitPullsListFilesResponse as response}
// Export mock function
export const fn = jest.fn((data: EndpointOptions) => {
return Promise.resolve({
data: OctokitPullsListFilesResponse
} as OctokitResponse<any>)
})

View File

@@ -0,0 +1,19 @@
import {fn as compareCommits} from './compareCommits'
import {
OctokitReposCompareCommitsRequest,
OctokitReposCompareCommitsResponse
} from '../payloads'
describe('Testing Octokit object...', () => {
beforeAll(() => {
jest.restoreAllMocks()
})
it('...repos.compareCommits(request) for push', () => {
const request = OctokitReposCompareCommitsRequest
const response = OctokitReposCompareCommitsResponse
return compareCommits(request).then(data => {
expect(data.data.files).toBe(response)
return expect(data.data.files.length).toBe(7)
})
})
})

View File

@@ -0,0 +1,16 @@
// Import Request and Response Objects
import {EndpointOptions, OctokitResponse} from '@octokit/types'
import {
// OctokitReposCompareCommitsRequest,
OctokitReposCompareCommitsResponse
} from '../payloads'
// Form and export Response Objects
export {OctokitReposCompareCommitsResponse as response}
// Export mock function
export const fn = jest.fn((data: EndpointOptions) => {
return Promise.resolve({
data: {
files: OctokitReposCompareCommitsResponse
}
} as OctokitResponse<any>)
})

View File

@@ -0,0 +1,61 @@
import {Env, p, getTestFiles, getTestEvents} from './mocks/env'
let env: Env
it('set default inputs', () => {
const input = p.changedFilesInput()
const defaultFormats = ['json', ',', ' ', '_<br />&nbsp;&nbsp;_']
expect(input.length).toBe(4)
input.forEach((t, i) => {
expect(t.events).toBe('pull')
if (Array.isArray(t.inputs)) {
expect(t.inputs[0]).toBe(defaultFormats[i])
expect(t.inputs[1]).toBe(p.normalFileArray)
if (defaultFormats[i] === 'json')
expect(t.inputs[2]).toBe(JSON.stringify(t.inputs[1]))
else if (Array.isArray(t.inputs[1]))
expect(t.inputs[2]).toBe(t.inputs[1].join(defaultFormats[i]))
}
})
expect(input[0].events).toBe('pull')
})
describe('Testing payloads.ts...', () => {
describe.each([
{files: ['/test/file', '/test/file2']},
{files: ['/test/&&&file', '/test/&&&file2']},
{files: ['/test/f&*(@mafile1', '/test/f&*(@mafile2']},
{files: ['/test/ke_c``ommands1', '/test/ke_c``ommands2']}
])('...changedFilesInput default with files %p...', files => {
describe.each(['json', ',', ' ', '_<br />&nbsp;&nbsp;_'])(
'...with format %s...',
format => {
it(`set default inputs files:${JSON.stringify(
files,
null,
2
)} format:${format}`, () => {
const input = p.changedFilesInput('pull', files.files, [format])
expect(input.length).toBe(1)
expect(input[0].events).toBe('pull')
if (Array.isArray(input[0].inputs)) {
expect(input[0].inputs[0]).toBe(format)
expect(input[0].inputs[1]).toBe(files.files)
if (format === 'json' && Array.isArray(input[0].inputs[2]))
expect(input[0].inputs[2]).toBe(
`[ "${input[0].inputs[2][0]}", "${input[0].inputs[2][1]}" ]`
)
else if (
Array.isArray(input[0].inputs[1]) &&
Array.isArray(input[0].inputs[2])
) {
expect(input[0].inputs[2]).toBe(
`${input[0].inputs[2][0]}${format}${input[0].inputs[2][1]}`
)
}
}
})
}
)
})
})

View File

@@ -0,0 +1,709 @@
// Imports
import {Inferred} from 'typings/Inferred'
import {TestInput} from 'typings/TestInput'
import * as p from './mocks/octokit/payloads'
/**
* Events to Test
*/
export const testEvents: string[] = [
'issue_comment_created',
'issue_comment_edited',
'pull_request_opened',
'pull_request_reopened',
'pull_request_synchronize',
'push_merge',
'push',
'schedule'
]
export function changedFilesInput(
event = 'pull',
files: string[] = p.normalFileArray,
formats: string[] = ['json', ',', ' ', '_<br />&nbsp;&nbsp;_']
): TestInput[] {
return formats.map(format => {
if (format === 'json')
return {
inputs: [format, files, JSON.stringify(files)],
events: event
} as TestInput
return {
inputs: [format, files, files.join(format)],
events: event
} as TestInput
})
}
/**
* FilesHelper Test inputs
*/
export const getFormatExtInputs: TestInput[] = [
{inputs: ['json', 'json', '.json'], events: 'push'},
{inputs: ['csv', ',', '.csv'], events: 'push'},
{inputs: ['txt', ' ', '.txt'], events: 'push'},
{inputs: ['txt_hard', '_<br />&nbsp;&nbsp;_', '.txt'], events: 'push'}
]
/**
* GithubHelper Test inputs
*/
export const initClientTestInputs: TestInput[] = [
{
inputs: [
'calls the Github client constructor with a token',
'12345abcde',
'12345abcde'
],
events: 'all'
},
{
inputs: ['calls the Github client constructor without a token', '', ''],
events: 'all'
}
]
export const getChangedPRFilesTestInputs: TestInput[] = [
{
inputs: [
'gets changed files for a pull request',
{owner: 'trilom', repo: 'file-changes-action', pullNumber: 83},
p.OctokitPaginatePrResponse
],
events: 'all'
},
{
inputs: [
'throws an error with no pull request',
{owner: 'trilom', repo: 'file-changes-action', pullNumber: NaN},
{
error: '404/HttpError',
from: 'getChangedPRFiles',
message:
'There was an error getting change files for repo:file-changes-action owner:trilom pr:NaN',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid repo for pull request',
{
owner: 'trilom',
repo: 'file-chandkdk-action-thatdoesntreallyexist',
pullNumber: 83
},
{
error: '404/HttpError',
from: 'getChangedPRFiles',
message:
'There was an error getting change files for repo:file-chandkdk-action-thatdoesntreallyexist owner:trilom pr:83',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid owner for pull request',
{
owner: 'this-isntareal-githubowner',
repo: 'file-changes-action',
pullNumber: 83
},
{
error: '404/HttpError',
from: 'getChangedPRFiles',
message:
'There was an error getting change files for repo:file-changes-action owner:this-isntareal-githubowner pr:83',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
}
]
export const getChangedPushFilesTestInputs: TestInput[] = [
{
inputs: [
'gets changed files for a push',
{
owner: 'trilom',
repo: 'file-changes-action',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
},
p.OctokitPaginatePushResponse
],
events: 'all'
},
{
inputs: [
'throws an error with no before for a push',
{
owner: 'trilom',
repo: 'file-changes-action',
before: '',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
},
{
error: '404/HttpError',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:file-changes-action owner:trilom base: head:4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with no after for a push',
{
owner: 'trilom',
repo: 'file-changes-action',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: ''
},
{
error: '404/HttpError',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:file-changes-action owner:trilom base:6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 head:',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid repo for a push',
{
owner: 'trilom',
repo: 'file-chandkdk-action-thatdoesntreallyexist',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
},
{
error: '404/HttpError',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:file-chandkdk-action-thatdoesntreallyexist owner:trilom base:6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 head:4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid owner for a push',
{
owner: 'this-isntareal-githubowner',
repo: 'file-changes-action',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
},
{
error: '404/HttpError',
from: 'getChangedPushFiles',
message:
'There was an error getting change files for repo:file-changes-action owner:this-isntareal-githubowner base:6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 head:4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
}
]
export const getChangedFilesTestInputs: TestInput[] = [
{
inputs: [
'gets changed files for a push',
{
repo: 'trilom/file-changes-action',
...({
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred)
},
p.OctokitPaginatePushResponse
],
events: 'all'
},
{
inputs: [
'throws an error with a malformed owner/repo for a push',
{
repo: 'trilom/testing/afew/backslash',
...({
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred)
},
{
error: '500/Unknown Error:Error',
from: 'getChangedFiles',
message:
'There was an error getting change files outputs pr: NaN before: 6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 after: 4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: JSON.stringify(
{
error: '500/Bad-Repo',
from: 'self',
message:
'Repo input of trilom/testing/afew/backslash has more than 2 length after splitting.',
payload: ''
},
null,
2
)
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid owner for a push',
{
repo: 'trilom-NOTREAL/backslash',
...({
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred)
},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:backslash owner:trilom-NOTREAL base:6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 head:4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with no after for a push',
{
repo: 'trilom/cloudformation',
...({before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2'} as Inferred)
},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:cloudformation owner:trilom base:6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2 head:',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with no before for a push',
{
repo: 'trilom/cloudformation',
...({after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'} as Inferred)
},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:cloudformation owner:trilom base: head:4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'gets changed files for a pull request',
{repo: 'trilom/file-changes-action', ...({pr: 83} as Inferred)},
p.OctokitPaginatePrResponse
],
events: 'all'
},
{
inputs: [
'throws an error with a malformed owner/repo for a pr',
{repo: 'trilom/testing/afew/backslash', ...({pr: 83} as Inferred)},
{
error: '500/Unknown Error:Error',
from: 'getChangedFiles',
message:
'There was an error getting change files outputs pr: 83 before: undefined after: undefined',
payload: JSON.stringify(
{
error: '500/Bad-Repo',
from: 'self',
message:
'Repo input of trilom/testing/afew/backslash has more than 2 length after splitting.',
payload: ''
},
null,
2
)
}
],
events: 'all'
},
{
inputs: [
'throws an error with invalid owner for a pr',
{repo: 'trilom-NOTREAL/backslash', ...({pr: 80} as Inferred)},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:backslash owner:trilom-NOTREAL pr:80',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with no pull request',
{repo: 'trilom/cloudformation', ...({} as Inferred)},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:cloudformation owner:trilom base: head:',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
},
{
inputs: [
'throws an error with no pull request',
{repo: 'trilom/cloudformation', ...({} as Inferred)},
{
error: '404/HttpError',
from: 'undefined/Error',
message:
'There was an error getting change files for repo:cloudformation owner:trilom base: head:',
payload: {name: 'HttpError', status: '404'}
}
],
events: 'all'
}
]
/**
* InputHelper Test inputs
*/
export const inputTestInputs: TestInput[] = [
{
inputs: [
'githubRepo',
'trilom-test/file-changes-action',
'trilom-test/file-changes-action'
],
events: 'all'
},
{inputs: ['githubToken', 'InputTestToken', 'InputTestToken'], events: 'all'},
{
inputs: [
'pushBefore',
'6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
'6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2'
],
events: 'all'
},
{
inputs: [
'pushAfter',
'4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
'4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
],
events: 'all'
},
{inputs: ['prNumber', '83', 83], events: 'all'},
{inputs: ['output', 'json', 'json'], events: 'all'},
{inputs: ['fileOutput', 'json', 'json'], events: 'all'}
]
export const inferTestInputs: TestInput[] = [
{
inputs: [
'sets PUSH inferred outputs with pr inputs and PUSH inputs and PULL_REQUEST event',
{
event: 'pull_request',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred
],
events: ['pull_request_opened', 'pull_request_reopened']
},
{
inputs: [
'sets PR inferred outputs with pr inputs and PUSH inputs and PULL_REQUEST_SYNCHRONIZE event',
{
event: 'pull_request',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred
],
events: ['pull_request_synchronize']
},
{
inputs: [
'sets PULL_REQUEST inferred outputs with single PUSH input and PULL_REQUEST event, ALSO WARN weird',
{
event: 'pull_request',
before: '787a72d40923de2f5308e7095ff9e6063fdbc219',
after: '',
pr: 83
},
{pr: 83} as Inferred
],
events: [
'pull_request_opened',
'pull_request_reopened',
'pull_request_synchronize'
]
},
{
inputs: [
'sets PULL_REQUEST inferred outputs with no PUSH inputs and PULL_REQUEST event',
{event: 'pull_request', before: '', after: '', pr: 83},
{pr: 83} as Inferred
],
events: [
'pull_request_opened',
'pull_request_reopened',
'pull_request_synchronize'
]
},
{
inputs: [
'sets PULL_REQUEST inferred outputs with pr input and PUSH event',
{
event: 'push',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{pr: 83} as Inferred
],
events: ['push', 'push_merge']
},
{
inputs: [
'sets PUSH inferred outputs with no pr input and PUSH event',
{
event: 'push',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: NaN
},
{
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred
],
events: ['push', 'push_merge']
},
{
inputs: [
'sets PUSH inferred outputs with PUSH and PULL_REQUEST inputs NOT PUSH or PULL_REQUEST event, ALSO WARN all',
{
event: 'schedule',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited']
},
{
inputs: [
'sets PUSH inferred outputs with PUSH and PULL_REQUEST inputs NOT PUSH or PULL_REQUEST event, ALSO WARN all',
{
event: 'schedule',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{pr: 83} as Inferred
],
events: ['schedule']
},
{
inputs: [
'sets PULL_REQUEST inferred outputs with single PUSH and PULL_REQUEST inputs NOT PUSH or PULL_REQUEST event, ALSO WARN weird',
{
event: 'schedule',
before: '',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: 83
},
{pr: 83} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
},
{
inputs: [
'sets PULL_REQUEST inferred outputs with PULL_REQUEST input NOT PUSH or PULL_REQUEST event',
{event: 'schedule', before: '', after: '', pr: 83},
{pr: 83} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
},
{
inputs: [
'sets PUSH inferred outputs with PUSH inputs NOT PUSH or PULL_REQUEST event',
{
event: 'schedule',
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968',
pr: NaN
},
{
before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
},
{
inputs: [
'throws ERROR with no inputs NOT PUSH or PULL_REQUEST event',
{before: '', after: '', pr: NaN},
{} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
},
{
inputs: [
'throws ERROR with single only before NOT PUSH or PULL_REQUEST event',
{before: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2', pr: NaN},
{} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
},
{
inputs: [
'throws ERROR with single only after NOT PUSH or PULL_REQUEST event',
{after: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968', pr: NaN},
{} as Inferred
],
events: ['issue_comment_created', 'issue_comment_edited', 'schedule']
}
]
/**
* UtilsHelper Test inputs
*/
export const errorMessageInputs: TestInput[] = [
{
inputs: [
'getInputs',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'getInputs'},
null,
2
),
'There was an getting action inputs.'
],
events: 'all'
},
{
inputs: [
'inferInput',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'inferInput'},
null,
2
),
'There was an issue inferring inputs to the action.'
],
events: 'all'
},
{
inputs: [
'initClient',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'initClient'},
null,
2
),
'There was an issue initilizing the github client.'
],
events: 'all'
},
{
inputs: [
'getChangedFiles',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'getChangedFiles'},
null,
2
),
'There was an issue getting changed files from Github.'
],
events: 'all'
},
{
inputs: [
'sortChangedFiles',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'sortChangedFiles'},
null,
2
),
'There was an issue sorting changed files from Github.'
],
events: 'all'
},
{
inputs: [
'writeFiles',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'writeFiles'},
null,
2
),
'There was an issue writing output files.'
],
events: 'all'
},
{
inputs: [
'writeOutput',
JSON.stringify(
{name: 'Error', message: 'Error', from: 'writeOutput'},
null,
2
),
'There was an issue writing output variables.'
],
events: 'all'
}
]
/**
* main Test inputs
*/
export const mainInputs: TestInput[] = [
{
inputs: [
'push',
{
pushBefore: '6ac7697cd1c4f23a08d4d4edbe7dab06b34c58a2',
pushAfter: '4ee1a1a2515f4ac1b90a56aaeb060b97f20c8968'
},
'push'
],
events: 'all'
},
{inputs: ['pull_request', {prNumber: '83'}, 'pull_request'], events: 'all'}
]
export {errorMessageInputs as mainErrorInputs}

View File

@@ -0,0 +1,6 @@
export interface ActionError {
error: string
from: string
message: string
payload: string
}

View File

@@ -0,0 +1,3 @@
export interface ChangedFiles {
[key: string]: string[]
}

View File

@@ -0,0 +1,11 @@
import {InputOptions} from '@actions/core'
export interface CoreMock {
setFailed: (message: string) => void
setOutput: (name: string, value: string) => void
getInput: (message: string, options?: InputOptions) => string | undefined
debug: (message: string) => void
warning: (message: string) => void
info: (message: string) => void
error: (message: string) => void
}

View File

@@ -0,0 +1,15 @@
export interface FsMock {
writeFileSync: (
path: string | number | Buffer | URL,
data: any,
options?:
| string
| {
encoding?: string | null | undefined
mode?: string | number | undefined
flag?: string | undefined
}
| null
| undefined
) => void
}

View File

@@ -0,0 +1,10 @@
export interface GitHubFile {
added: string
modified: string
removed: string
renamed: string
filename: string
status: string
previous_filename: string
distinct: boolean
}

View File

@@ -0,0 +1,7 @@
import {Context} from '@actions/github/lib/context'
import {OctokitMock} from 'typings/OctokitMock'
export interface GitHubMock {
GitHub: (token: string) => OctokitMock | OctokitMock
context: Context
}

View File

@@ -0,0 +1,6 @@
export interface Inferred {
pr?: number
before?: string
after?: string
[key: string]: string | number | undefined
}

View File

@@ -0,0 +1,11 @@
export interface Inputs {
githubRepo: string
githubToken: string
pushBefore: string
pushAfter: string
prNumber: number
output: string
fileOutput: string
event: string
[key: string]: string | number
}

View File

@@ -0,0 +1,24 @@
import {EndpointOptions, RequestOptions, OctokitResponse} from '@octokit/types'
export interface OctokitMock {
paginate: (
data: EndpointOptions,
cb?: (response: OctokitResponse<any>) => Promise<any[]>
) => Promise<any[]>
pulls: {
listFiles: {
(data: EndpointOptions): Promise<OctokitResponse<any>>
endpoint: {
merge: (data: EndpointOptions) => RequestOptions
}
}
}
repos: {
compareCommits: {
(data: EndpointOptions): Promise<OctokitResponse<any>>
endpoint: {
merge: (data: EndpointOptions) => RequestOptions
}
}
}
}

View File

@@ -0,0 +1,10 @@
import {Inferred} from 'typings/Inferred'
/**
* @interface TestInput
* @param inputs test input to parse. Can be a string array or an array of objects
* @param event event to parse input for.
*/
export interface TestInput {
inputs: string[] | string[][] | object | Inferred[]
events: string | string[]
}