Files
deploy-pages/src/__tests__/internal/deployment.test.js
T

1075 lines
38 KiB
JavaScript
Raw Normal View History

2023-04-14 20:43:44 +01:00
const core = require('@actions/core')
2023-12-21 16:10:50 -05:00
// For mocking network calls with core http (http-client)
const nock = require('nock')
// For mocking network calls with native Fetch (octokit)
const { MockAgent, setGlobalDispatcher } = require('undici')
2023-04-14 20:43:44 +01:00
2023-05-22 17:00:13 +00:00
const { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION } = require('../../internal/deployment')
2023-04-14 20:43:44 +01:00
const fakeJwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
2023-12-21 16:10:50 -05:00
const LIST_ARTIFACTS_TWIRP_PATH = '/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts';
2023-04-14 20:43:44 +01:00
describe('Deployment', () => {
let mockPool
2023-04-14 21:01:12 +01:00
beforeEach(() => {
jest.clearAllMocks()
2023-04-14 21:01:12 +01:00
process.env.GITHUB_RUN_ID = '123'
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
process.env.GITHUB_TOKEN = 'gha-token'
process.env.GITHUB_SHA = '123abc'
process.env.GITHUB_ACTOR = 'monalisa'
process.env.GITHUB_ACTION = '__monalisa/octocat'
process.env.GITHUB_ACTION_PATH = 'something'
// A valid actions token must have an 'scp' field whose value is a space-delimited list of strings
process.env.ACTIONS_RUNTIME_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY3AiOiJBY3Rpb25zLkV4YW1wbGVTY29wZSBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCJ9.l-VcBU1PeNk_lWpOhjWehQlYyjCcY2dp_EMt7Rf06io"
process.env.ACTIONS_RESULTS_URL = "https://actions-results-url.biz"
2023-04-14 21:01:12 +01:00
jest.spyOn(core, 'getInput').mockImplementation(param => {
switch (param) {
case 'artifact_name':
return 'github-pages'
case 'token':
return process.env.GITHUB_TOKEN
2023-12-06 14:51:36 -06:00
case 'reporting_interval':
return 50 // Lower reporting interval to speed up test
2023-04-14 21:01:12 +01:00
default:
return process.env[`INPUT_${param.toUpperCase()}`] || ''
}
2023-04-14 20:43:44 +01:00
})
2023-04-14 20:58:13 +01:00
2023-04-14 21:01:12 +01:00
jest.spyOn(core, 'setOutput').mockImplementation(param => {
return param
2023-04-14 20:43:44 +01:00
})
2023-04-14 21:01:12 +01:00
jest.spyOn(core, 'setFailed').mockImplementation(param => {
return param
})
2023-04-14 21:01:12 +01:00
// Mock error/warning/info/debug
jest.spyOn(core, 'error').mockImplementation(jest.fn())
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
jest.spyOn(core, 'info').mockImplementation(jest.fn())
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
// Set up Fetch mocking
2023-12-21 16:10:50 -05:00
let mockAgent = new MockAgent()
mockAgent.disableNetConnect()
setGlobalDispatcher(mockAgent)
mockPool = mockAgent.get('https://api.github.com')
2023-04-14 21:01:12 +01:00
})
describe('#create', () => {
afterEach(() => {
// Remove mock for `core.getInput('preview')`
delete process.env.INPUT_PREVIEW
})
it('can successfully create a deployment', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA &&
body.oidc_token === fakeJwt
)
}
2023-04-14 21:01:12 +01:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-04-14 21:01:12 +01:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
expect(core.setFailed).not.toHaveBeenCalled()
expect(core.info).toHaveBeenLastCalledWith(
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 20:43:44 +01:00
})
2023-04-14 21:01:12 +01:00
it('can successfully create a preview deployment', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 4 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
keys[3] === 'preview' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA &&
body.oidc_token === fakeJwt &&
body.preview === true
)
}
2023-04-14 21:01:12 +01:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome',
preview_url: 'https://actions.drafts.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-04-14 21:01:12 +01:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Return `"true"` for `core.getInput("preview")`
process.env.INPUT_PREVIEW = 'true'
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
expect(core.setFailed).not.toHaveBeenCalled()
expect(core.info).toHaveBeenLastCalledWith(
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
2023-10-30 14:58:12 -04:00
it('reports errors with failed artifact metadata exchange', async () => {
2023-04-14 21:01:12 +01:00
process.env.GITHUB_SHA = 'invalid-build-version'
2023-10-30 14:58:12 -04:00
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(400, { message: 'Bad request' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
// Create the deployment
const deployment = new Deployment()
await expect(deployment.create()).rejects.toEqual(
new Error(
2023-10-30 14:58:12 -04:00
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
2023-04-14 21:01:12 +01:00
)
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
it('reports errors with a failed 500 in a deployment', async () => {
process.env.GITHUB_SHA = 'build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 2 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(500, { message: 'oh no' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
// Create the deployment
const deployment = new Deployment()
await expect(deployment.create()).rejects.toEqual(
new Error(
`Failed to create deployment (status: 500) with build version ${process.env.GITHUB_SHA}. Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
)
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
it('reports errors with an unexpected 403 during deployment', async () => {
process.env.GITHUB_SHA = 'build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 2 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(403, { message: 'You are forbidden' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
// Create the deployment
const deployment = new Deployment()
await expect(deployment.create()).rejects.toEqual(
new Error(
`Failed to create deployment (status: 403) with build version ${process.env.GITHUB_SHA}. Ensure GITHUB_TOKEN has permission "pages: write".`
)
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
it('reports errors with an unexpected 404 during deployment', async () => {
process.env.GITHUB_SHA = 'build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 2 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(404, { message: 'Not found' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
// Create the deployment
const deployment = new Deployment()
await expect(deployment.create()).rejects.toEqual(
new Error(
`Failed to create deployment (status: 404) with build version ${process.env.GITHUB_SHA}. Ensure GitHub Pages has been enabled: https://github.com/actions/is-awesome/settings/pages`
)
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
it('reports errors with failed deployments', async () => {
process.env.GITHUB_SHA = 'invalid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 2 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(400, { message: 'Bad request' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
// Create the deployment
const deployment = new Deployment()
await expect(deployment.create()).rejects.toEqual(
new Error(
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
)
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 20:43:44 +01:00
})
2023-10-30 14:58:12 -04:00
it('fails if there are multiple artifacts with the same name', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
2023-12-21 16:10:50 -05:00
total_count: 1,
2023-12-15 10:50:33 -05:00
artifacts: [
2023-12-21 18:43:25 -05:00
{ databaseId: 13, name: `github-pages`, size: 1400 },
{ databaseId: 14, name: `github-pages`, size: 1620 },
2023-12-15 10:50:33 -05:00
]
},
{ headers: { 'content-type': 'application/json' } }
)
2023-10-30 14:58:12 -04:00
const deployment = new Deployment()
await expect(deployment.create(fakeJwt)).rejects.toThrow(
2023-12-21 16:10:50 -05:00
`Multiple artifacts unexpectedly found for this workflow run. Artifact count is 2.`
2023-10-30 14:58:12 -04:00
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-10-30 14:58:12 -04:00
})
it('fails if there are no artifacts found', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 0,
artifacts: []
},
{ headers: { 'content-type': 'application/json' } }
)
2023-10-30 14:58:12 -04:00
const deployment = new Deployment()
await expect(deployment.create(fakeJwt)).rejects.toThrow(
2023-12-21 16:10:50 -05:00
`No artifacts found for this workflow run. Ensure artifacts are uploaded with actions/artifact@v4 or later.`
2023-10-30 14:58:12 -04:00
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-10-30 14:58:12 -04:00
})
2023-10-30 15:35:32 -04:00
it('fails with error message if list artifact endpoint returns 500', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(500, { message: 'oh no' }, { headers: { 'content-type': 'application/json' } })
2023-10-30 15:35:32 -04:00
const deployment = new Deployment()
await expect(deployment.create(fakeJwt)).rejects.toThrow(
`Failed to create deployment (status: 500) with build version valid-build-version. Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-10-30 15:35:32 -04:00
})
2023-05-22 17:00:13 +00:00
it('warns if the artifact size is bigger than maximum', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
const artifactSize = ONE_GIGABYTE + 1
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 12, name: `github-pages`, size: artifactSize }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-05-22 17:00:13 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 12 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-05-22 17:00:13 +00:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-05-22 17:00:13 +00:00
const deployment = new Deployment()
await deployment.create(fakeJwt)
expect(core.warning).toBeCalledWith(
`Uploaded artifact size of ${artifactSize} bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}. Deployment might fail.`
)
expect(core.setFailed).not.toHaveBeenCalled()
expect(core.info).toHaveBeenLastCalledWith(
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-05-22 17:00:13 +00:00
})
it('warns when the timeout is greater than the maximum allowed', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Set timeout to greater than max
jest.spyOn(core, 'getInput').mockImplementation(param => {
switch (param) {
case 'artifact_name':
return 'github-pages'
case 'token':
return process.env.GITHUB_TOKEN
2023-12-06 14:51:36 -06:00
case 'reporting_interval':
return 50 // Lower reporting interval to speed up test
case 'timeout':
2023-05-12 16:28:50 +00:00
return MAX_TIMEOUT + 1
default:
return process.env[`INPUT_${param.toUpperCase()}`] || ''
}
})
const deployment = new Deployment()
await deployment.create(fakeJwt)
expect(core.warning).toBeCalledWith(
2023-05-12 16:28:50 +00:00
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
)
2023-12-21 16:10:50 -05:00
twirpScope.done()
})
2023-04-14 20:43:44 +01:00
})
2023-04-14 21:01:12 +01:00
describe('#check', () => {
it('sets output to success when deployment is successful', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
method: 'GET'
2023-04-14 21:01:12 +01:00
})
.reply(200, { status: 'succeed' }, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
await deployment.check()
expect(core.setOutput).toBeCalledWith('status', 'succeed')
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
2023-04-14 21:04:16 +01:00
it('fails check when no deployment is found', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
const deployment = new Deployment()
await deployment.check()
expect(core.setFailed).toBeCalledWith('Deployment not found.')
})
it('exits early when deployment is not in progress', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
const deployment = new Deployment()
await deployment.create(fakeJwt)
deployment.deploymentInfo.pending = false
await deployment.check()
expect(core.setFailed).toBeCalledWith('Unable to get deployment status.')
2023-12-21 16:10:50 -05:00
twirpScope.done()
})
2023-05-11 18:39:06 +00:00
it('enforces max timeout', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-05-11 18:39:06 +00:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
method: 'GET'
})
.reply(200, { status: 'deployment_in_progress' }, { headers: { 'content-type': 'application/json' } })
.times(2)
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`,
method: 'POST'
})
.reply(200, {}, { headers: { 'content-type': 'application/json' } })
2023-05-11 18:39:06 +00:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Set timeout to greater than max
2023-05-11 18:39:06 +00:00
jest.spyOn(core, 'getInput').mockImplementation(param => {
switch (param) {
case 'artifact_name':
return 'github-pages'
case 'token':
return process.env.GITHUB_TOKEN
2023-05-12 16:28:50 +00:00
case 'error_count':
return 10
2023-12-06 14:51:36 -06:00
case 'reporting_interval':
return 50 // Lower the interval for the test
2023-05-11 18:39:06 +00:00
case 'timeout':
2023-05-12 16:28:50 +00:00
return MAX_TIMEOUT + 1
2023-05-11 18:39:06 +00:00
default:
return process.env[`INPUT_${param.toUpperCase()}`] || ''
}
})
2023-12-06 14:51:36 -06:00
// Jump the "current time" by MAX_TIMEOUT ever time Date.now is called
const _now = Date.now
let nowCalls = 0
2023-12-06 14:56:54 -06:00
const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => {
nowCalls++
return _now() + MAX_TIMEOUT * nowCalls
})
2023-05-11 18:39:06 +00:00
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
await deployment.check()
2023-12-06 14:51:36 -06:00
nowSpy.mockRestore()
2023-05-12 16:28:50 +00:00
expect(deployment.timeout).toEqual(MAX_TIMEOUT)
2023-05-11 18:39:06 +00:00
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-05-11 18:39:06 +00:00
})
it('sets timeout to user timeout if user timeout is less than max timeout', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-05-11 18:39:06 +00:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`,
method: 'POST'
})
.reply(200, {}, { headers: { 'content-type': 'application/json' } })
2023-05-11 18:39:06 +00:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Set timeout to greater than max
2023-05-11 18:39:06 +00:00
jest.spyOn(core, 'getInput').mockImplementation(param => {
switch (param) {
case 'artifact_name':
return 'github-pages'
case 'token':
return process.env.GITHUB_TOKEN
2023-05-12 16:28:50 +00:00
case 'error_count':
return 10
2023-12-06 01:24:29 -06:00
case 'reporting_interval':
return 42 // The default of 5000 is too long for the test
2023-05-11 18:39:06 +00:00
case 'timeout':
return 42
default:
return process.env[`INPUT_${param.toUpperCase()}`] || ''
}
})
const now = Date.now()
const mockStartTime = now - 42
jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => mockStartTime)
.mockImplementationOnce(() => now)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
await deployment.check()
expect(deployment.timeout).toEqual(42)
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-05-11 18:39:06 +00:00
})
it('sets output to success when timeout is set but not reached', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-05-11 18:39:06 +00:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-05-11 18:39:06 +00:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
method: 'GET'
2023-05-11 18:39:06 +00:00
})
.reply(200, { status: 'succeed' }, { headers: { 'content-type': 'application/json' } })
2023-05-11 18:39:06 +00:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Set timeout to greater than max
2023-05-11 18:39:06 +00:00
jest.spyOn(core, 'getInput').mockImplementation(param => {
switch (param) {
case 'artifact_name':
return 'github-pages'
case 'token':
return process.env.GITHUB_TOKEN
2023-05-12 16:28:50 +00:00
case 'error_count':
return 10
2023-12-06 01:24:29 -06:00
case 'reporting_interval':
return 0 // The default of 5000 is too long for the test
2023-05-11 18:39:06 +00:00
case 'timeout':
return 42
default:
return process.env[`INPUT_${param.toUpperCase()}`] || ''
}
})
const now = Date.now()
const mockStartTime = now // No time elapsed
2023-05-11 18:39:06 +00:00
jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => mockStartTime)
.mockImplementationOnce(() => now)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
await deployment.check()
expect(deployment.timeout).toEqual(42)
expect(core.error).not.toBeCalled()
expect(core.setOutput).toBeCalledWith('status', 'succeed')
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-05-11 18:39:06 +00:00
})
2023-04-14 21:01:12 +01:00
})
describe('#cancel', () => {
it('can successfully cancel a deployment', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
2023-04-14 21:01:12 +01:00
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
2023-04-14 21:01:12 +01:00
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`,
method: 'POST'
})
.reply(200, {}, { headers: { 'content-type': 'application/json' } })
2023-04-14 21:01:12 +01:00
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
// Cancel it
await deployment.cancel()
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
2023-12-21 16:10:50 -05:00
twirpScope.done()
2023-04-14 21:01:12 +01:00
})
it('can exit if a pages deployment was not created and none need to be cancelled', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
// Create the deployment
const deployment = new Deployment()
// Cancel it
await deployment.cancel()
expect(core.debug).toHaveBeenCalledWith('all variables are set')
expect(core.debug).toHaveBeenCalledWith(`No deployment to cancel`)
})
it('catches an error when trying to cancel a deployment', async () => {
process.env.GITHUB_SHA = 'valid-build-version'
2023-12-21 16:10:50 -05:00
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
.post(LIST_ARTIFACTS_TWIRP_PATH)
2023-12-15 10:54:04 -05:00
.reply(
200,
2023-12-15 10:50:33 -05:00
{
total_count: 1,
2023-12-21 18:43:25 -05:00
artifacts: [{ databaseId: 11, name: `github-pages`, size: 221 }]
2023-12-15 10:50:33 -05:00
},
{ headers: { 'content-type': 'application/json' } }
2023-10-30 14:58:12 -04:00
)
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`,
method: 'POST',
body: bodyString => {
const body = JSON.parse(bodyString)
const keys = Object.keys(body).sort()
return (
keys.length === 3 &&
2023-12-15 10:50:33 -05:00
keys[0] === 'artifact_id' &&
keys[1] === 'oidc_token' &&
keys[2] === 'pages_build_version' &&
2023-12-15 10:50:33 -05:00
body.artifact_id === 11 &&
body.oidc_token === fakeJwt &&
body.pages_build_version === process.env.GITHUB_SHA
)
}
})
.reply(
200,
{
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
page_url: 'https://actions.github.io/is-awesome'
},
{ headers: { 'content-type': 'application/json' } }
)
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`,
method: 'POST'
})
.reply(500, {}, { headers: { 'content-type': 'application/json' } })
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
// Create the deployment
const deployment = new Deployment()
await deployment.create(fakeJwt)
// Cancel it
await deployment.cancel()
expect(core.error).toHaveBeenCalledWith(`Canceling Pages deployment failed`, expect.anything())
2023-12-21 16:10:50 -05:00
twirpScope.done()
})
2023-04-14 21:01:12 +01:00
})
})