Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration tests for main.js #56

Merged
merged 21 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f37c60c
test: Add test to verify 'main' exits with an error when 'GITHUB_REPO…
smockle Oct 3, 2023
6ffe98f
test: Add tests to verify 'main' overrides 'request'’s default 'baseU…
smockle Oct 3, 2023
0d62a31
Revert "test: Add tests to verify 'main' overrides 'request'’s defaul…
smockle Oct 4, 2023
3bbba10
test: Add tests to verify 'main' gets a token.
smockle Oct 4, 2023
f4530b0
chore: Fix 'package-lock.json' after rebase
smockle Oct 5, 2023
b6d9941
test: Add tests for 'owner'/'repository' input permutations
smockle Oct 6, 2023
a851e91
test: Add tests for 'owner'-as-org/'owner'-as-user input permutations
smockle Oct 6, 2023
953f7f5
fix: DRY the invalidated private key to make tests more concise.
smockle Oct 6, 2023
7fc0201
fix: DRY the common test setup to make tests more concise.
smockle Oct 6, 2023
ebc6fac
fix: Remove unused imports
smockle Oct 6, 2023
9776690
docs: Use clearer descriptions of tests
smockle Oct 6, 2023
fe08922
docs: Move test scenario description comment for consistency.
smockle Oct 6, 2023
f63553c
fix: Make individual tests more explicit.
smockle Oct 6, 2023
39131f2
fix: Make base installation id mock more readable.
smockle Oct 6, 2023
57b2c90
chore: Re-run checks
smockle Oct 6, 2023
1174dff
fix: Set JSON response header
smockle Oct 6, 2023
73aade4
chore: Update ava snapshots
smockle Oct 6, 2023
10b4ea2
fix: Clear Actions environment variables (in tests) that change 'core…
smockle Oct 6, 2023
f343cd5
fix: Move Actions env var override, and ensure it’s cleared when test…
smockle Oct 6, 2023
02f90f5
fix: Remove '// @ts-check' in test files, in part to suppress the war…
smockle Oct 6, 2023
fabe540
fix: Remove async IIFE wrapper; use top-level 'await'
smockle Oct 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ const tests = readdirSync("tests").filter((file) => file.endsWith(".test.js"));

for (const file of tests) {
test(file, async (t) => {
const { stderr, stdout } = await execa("node", [`tests/${file}`]);
// Override Actions environment variables that change `core`’s behavior
const env = {
GITHUB_OUTPUT: undefined,
GITHUB_STATE: undefined,
};
const { stderr, stdout } = await execa("node", [`tests/${file}`], { env });
t.snapshot(stderr, "stderr");
t.snapshot(stdout, "stdout");
});
Expand Down
9 changes: 9 additions & 0 deletions tests/main-missing-owner.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
process.env.GITHUB_REPOSITORY = "actions/create-github-app-token";
delete process.env.GITHUB_REPOSITORY_OWNER;

// Verify `main` exits with an error when `GITHUB_REPOSITORY_OWNER` is missing.
try {
await import("../main.js");
} catch (error) {
console.error(error.message);
}
8 changes: 8 additions & 0 deletions tests/main-missing-repository.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
delete process.env.GITHUB_REPOSITORY;

// Verify `main` exits with an error when `GITHUB_REPOSITORY` is missing.
try {
await import("../main.js");
} catch (error) {
console.error(error.message);
}
7 changes: 7 additions & 0 deletions tests/main-token-get-owner-set-repo-set-to-many.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when the `owner` and `repositories` inputs are set (and the latter is a list of repos).
await test(() => {
process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER;
process.env.INPUT_REPOSITORIES = `${process.env.GITHUB_REPOSITORY},actions/toolkit`;
});
7 changes: 7 additions & 0 deletions tests/main-token-get-owner-set-repo-set-to-one.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when the `owner` and `repositories` inputs are set (and the latter is a single repo).
await test(() => {
process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER;
process.env.INPUT_REPOSITORIES = process.env.GITHUB_REPOSITORY;
});
25 changes: 25 additions & 0 deletions tests/main-token-get-owner-set-to-org-repo-unset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when the `owner` input is set (to an org), but the `repositories` input isn’t set.
await test((mockPool) => {
process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER;
delete process.env.INPUT_REPOSITORIES;

// Mock installation id request
const mockInstallationId = "123456";
mockPool
.intercept({
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
method: "GET",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(
200,
{ id: mockInstallationId },
{ headers: { "content-type": "application/json" } }
);
});
36 changes: 36 additions & 0 deletions tests/main-token-get-owner-set-to-user-repo-unset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when the `owner` input is set (to a user), but the `repositories` input isn’t set.
await test((mockPool) => {
process.env.INPUT_OWNER = "smockle";
delete process.env.INPUT_REPOSITORIES;

// Mock installation id request
const mockInstallationId = "123456";
mockPool
.intercept({
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
method: "GET",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(404);
mockPool
.intercept({
path: `/users/${process.env.INPUT_OWNER}/installation`,
method: "GET",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(
200,
{ id: mockInstallationId },
{ headers: { "content-type": "application/json" } }
);
});
7 changes: 7 additions & 0 deletions tests/main-token-get-owner-unset-repo-set.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when the `owner` input is not set, but the `repositories` input is set.
await test(() => {
delete process.env.INPUT_OWNER;
process.env.INPUT_REPOSITORIES = process.env.GITHUB_REPOSITORY;
});
25 changes: 25 additions & 0 deletions tests/main-token-get-owner-unset-repo-unset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { test } from "./main.js";

// Verify `main` successfully obtains a token when neither the `owner` nor `repositories` input is set.
await test((mockPool) => {
delete process.env.INPUT_OWNER;
delete process.env.INPUT_REPOSITORIES;

// Mock installation id request
const mockInstallationId = "123456";
mockPool
.intercept({
path: `/repos/${process.env.GITHUB_REPOSITORY}/installation`,
method: "GET",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(
200,
{ id: mockInstallationId },
{ headers: { "content-type": "application/json" } }
);
});
96 changes: 96 additions & 0 deletions tests/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Base for all `main` tests.
// @ts-check
import { MockAgent, setGlobalDispatcher } from "undici";

export async function test(cb = (_mockPool) => {}) {
// Set required environment variables and inputs
process.env.GITHUB_REPOSITORY_OWNER = "actions";
process.env.GITHUB_REPOSITORY = "actions/create-github-app-token";
// inputs are set as environment variables with the prefix INPUT_
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#example-specifying-inputs
process.env.INPUT_APP_ID = "123456";
process.env.INPUT_PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA280nfuUM9w00Ib9E2rvZJ6Qu3Ua3IqR34ZlK53vn/Iobn2EL
Z9puc5Q/nFBU15NKwHyQNb+OG2hTCkjd1Xi9XPzEOH1r42YQmTGq8YCkUSkk6KZA
5dnhLwN9pFquT9fQgrf4r1D5GJj3rqvj8JDr1sBmunArqY5u4gziSrIohcjLIZV0
cIMz/RUIMe/EAsNeiwzEteHAtf/WpMs+OfF94SIUrDlkPr0H0H3DER8l1HZAvE0e
eD3ZJ6njrF6UHQWDVrekSTB0clpVTTU9TMpe+gs2nnFww9G8As+WsW8xHVjVipJy
AwqBhiR+s7wlcbh2i0NQqt8GL9/jIFTmleiwsQIDAQABAoIBAHNyP8pgl/yyzKzk
/0871wUBMTQ7zji91dGCaFtJM0HrcDK4D/uOOPEv7nE1qDpKPLr5Me1pHUS7+NGw
EAPtlNhgUtew2JfppdIwyi5qeOPADoi7ud6AH8xHsxg+IMwC+JuP8WhzyUHoJj9y
PRi/pX94Mvy9qdE25HqKddjx1mLdaHhxqPkr16/em23uYZqm1lORsCPD3vTlthj7
WiEbBSqmpYvjj8iFP4yFk2N+LvuWgSilRzq1Af3qE7PUp4xhjmcOPs78Sag1T7nl
ww/pgqCegISABHik7j++/5T+UlI5cLsyp/XENU9zAd4kCIczwNKpun2bn+djJdft
ravyX4ECgYEA+k2mHfi1zwKF3wT+cJbf30+uXrJczK2yZ33//4RKnhBaq7nSbQAI
nhWz2JESBK0TEo0+L7yYYq3HnT9vcES5R1NxzruH9wXgxssSx3JUj6w1raXYPh3B
+1XpYQsa6/bo2LmBELEx47F8FQbNgD5dmTJ4jBrf6MV4rRh9h6Bs7UkCgYEA4M3K
eAw52c2MNMIxH/LxdOQtEBq5GMu3AQC8I64DSSRrAoiypfEgyTV6S4gWJ5TKgYfD
zclnOVJF+tITe3neO9wHoZp8iCjCnoijcT6p2cJ4IaW68LEHPOokWBk0EpLjF4p2
7sFi9+lUpXYXfCN7aMJ77QpGzB7dNsBf0KUxMCkCgYEAjw/mjGbk82bLwUaHby6s
0mQmk7V6WPpGZ+Sadx7TzzglutVAslA8nK5m1rdEBywtJINaMcqnhm8xEm15cj+1
blEBUVnaQpQ3fyf+mcR9FIknPRL3X7l+b/sQowjH4GqFd6m/XR0KGMwO0a3Lsyry
MGeqgtmxdMe5S6YdyXEmERECgYAgQsgklDSVIh9Vzux31kh6auhgoEUh3tJDbZSS
Vj2YeIZ21aE1mTYISglj34K2aW7qSc56sMWEf18VkKJFHQccdgYOVfo7HAZZ8+fo
r4J2gqb0xTDfq7gLMNrIXc2QQM4gKbnJp60JQM3p9NmH8huavBZGvSvNzTwXyGG3
so0tiQKBgGQXZaxaXhYUcxYHuCkQ3V4Vsj3ezlM92xXlP32SGFm3KgFhYy9kATxw
Cax1ytZzvlrKLQyQFVK1COs2rHt7W4cJ7op7C8zXfsigXCiejnS664oAuX8sQZID
x3WQZRiXlWejSMUAHuMwXrhGlltF3lw83+xAjnqsVp75kGS6OH61
-----END RSA PRIVATE KEY-----`; // This key is invalidated. It’s from https://github.com/octokit/auth-app.js/issues/465#issuecomment-1564998327.

// Set up mocking
const mockAgent = new MockAgent();
mockAgent.disableNetConnect();
setGlobalDispatcher(mockAgent);
const mockPool = mockAgent.get("https://api.github.com");

// Calling `auth({ type: "app" })` to obtain a JWT doesn’t make network requests, so no need to intercept.

// Mock installation id request
const mockInstallationId = "123456";
const owner = process.env.INPUT_OWNER ?? process.env.GITHUB_REPOSITORY_OWNER;
const repo = encodeURIComponent(
(process.env.INPUT_REPOSITORIES ?? process.env.GITHUB_REPOSITORY).split(
","
)[0]
);
mockPool
.intercept({
path: `/repos/${owner}/${repo}/installation`,
method: "GET",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(
200,
{ id: mockInstallationId },
{ headers: { "content-type": "application/json" } }
);

// Mock installation access token request
const mockInstallationAccessToken =
"ghs_16C7e42F292c6912E7710c838347Ae178B4a"; // This token is invalidated. It’s from https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app.
mockPool
.intercept({
path: `/app/installations/${mockInstallationId}/access_tokens`,
method: "POST",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": "actions/create-github-app-token",
// Note: Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
},
})
.reply(
201,
{ token: mockInstallationAccessToken },
{ headers: { "content-type": "application/json" } }
);

// Run the callback
cb(mockPool);

// Run the main script
await import("../main.js");
}
104 changes: 104 additions & 0 deletions tests/snapshots/index.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,110 @@ The actual snapshot is saved in `index.js.snap`.

Generated by [AVA](https://avajs.dev).

## main-missing-owner.test.js

> stderr

'GITHUB_REPOSITORY_OWNER missing, must be set to \'<owner>\''

> stdout

''

## main-missing-repository.test.js

> stderr

'GITHUB_REPOSITORY missing, must be set to \'<owner>/<repo>\''

> stdout

''

## main-token-get-owner-set-repo-set-to-many.test.js

> stderr

''

> stdout

`owner and repositories set, creating token for repositories "actions/create-github-app-token,actions/toolkit" owned by "actions"␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## main-token-get-owner-set-repo-set-to-one.test.js

> stderr

''

> stdout

`owner and repositories set, creating token for repositories "actions/create-github-app-token" owned by "actions"␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## main-token-get-owner-set-to-org-repo-unset.test.js

> stderr

''

> stdout

`repositories not set, creating token for all repositories for given owner "actions"␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## main-token-get-owner-set-to-user-repo-unset.test.js

> stderr

''

> stdout

`repositories not set, creating token for all repositories for given owner "smockle"␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## main-token-get-owner-unset-repo-set.test.js

> stderr

''

> stdout

`owner not set, creating owner for given repositories "actions/create-github-app-token" in current owner ("actions")␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## main-token-get-owner-unset-repo-unset.test.js

> stderr

''

> stdout

`owner and repositories not set, creating token for the current repository ("create-github-app-token")␊
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a`

## post-token-set.test.js

> stderr
Expand Down
Binary file modified tests/snapshots/index.js.snap
Binary file not shown.