mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-09-02 05:55:10 +09:00
chore(deps): bump @actions/core from 2.0.3 to 3.0.1 (#1746)
* chore(deps): bump @actions/core from 2.0.3 to 3.0.1 Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 2.0.3 to 3.0.1. - [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md) - [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core) --- updated-dependencies: - dependency-name: "@actions/core" dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: update test mocks for @actions/core ESM @actions/core v3 ships as an ESM module with non-configurable exports, breaking vi.spyOn(). Switch to vi.mock('@actions/core') which intercepts at the module loader level. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tom Keller <kellertk@amazon.com>
This commit is contained in:
+8
-13
@@ -5,20 +5,15 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { cleanup } from '../src/cleanup';
|
||||
import mocks from './mockinputs.test';
|
||||
|
||||
vi.mock('@actions/core');
|
||||
|
||||
const mockedSTSClient = mockClient(STSClient);
|
||||
|
||||
describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
beforeEach(() => {
|
||||
// Reset mock state
|
||||
vi.restoreAllMocks();
|
||||
vi.resetAllMocks();
|
||||
mockedSTSClient.reset();
|
||||
// Mock GitHub Actions core functions
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation((_s) => {});
|
||||
vi.spyOn(core, 'setFailed').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'setOutput').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'debug').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'info').mockImplementation((_m) => {});
|
||||
vi.mocked(core.getInput).mockReturnValue('');
|
||||
process.env = {
|
||||
...mocks.envs,
|
||||
AWS_ACCESS_KEY_ID: 'CLEANUPTEST',
|
||||
@@ -39,7 +34,7 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', '');
|
||||
});
|
||||
it('also clears AWS_PROFILE when aws-profile was set', {}, () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
vi.mocked(core.getInput).mockImplementation((name: string) => {
|
||||
if (name === 'aws-profile') return 'my-profile';
|
||||
if (name === 'output-env-credentials') return 'true';
|
||||
return '';
|
||||
@@ -50,7 +45,7 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_PROFILE', '');
|
||||
});
|
||||
it('skips env cleanup when aws-profile is set without output-env-credentials', {}, () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
vi.mocked(core.getInput).mockImplementation((name: string) => {
|
||||
if (name === 'aws-profile') return 'my-profile';
|
||||
return '';
|
||||
});
|
||||
@@ -59,14 +54,14 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
it('handles errors', {}, () => {
|
||||
vi.spyOn(core, 'exportVariable').mockImplementationOnce(() => {
|
||||
vi.mocked(core.exportVariable).mockImplementationOnce(() => {
|
||||
throw new Error('Test error');
|
||||
});
|
||||
cleanup();
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it(`doesn't export credentials as empty env variables if asked not to`, {}, () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
cleanup();
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
+10
-17
@@ -2,10 +2,11 @@ import * as core from '@actions/core';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import * as helpers from '../src/helpers';
|
||||
|
||||
vi.mock('@actions/core');
|
||||
|
||||
describe('Configure AWS Credentials helpers', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.spyOn(core, 'debug').mockImplementation(() => {});
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
it('removes brackets from GitHub Actor', {}, () => {
|
||||
const actor = 'actor[bot]';
|
||||
@@ -24,9 +25,6 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('can output creds when told to', {}, () => {
|
||||
vi.spyOn(core, 'setOutput').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
||||
helpers.exportCredentials(
|
||||
{ AccessKeyId: 'test', SecretAccessKey: 'test', SessionToken: 'test', Expiration: new Date(8640000000000000) },
|
||||
true,
|
||||
@@ -47,9 +45,6 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
process.env = env;
|
||||
});
|
||||
it(`won't output credentials to env if told not to`, {}, () => {
|
||||
vi.spyOn(core, 'setOutput').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
||||
helpers.exportCredentials(
|
||||
{ AccessKeyId: 'test', SecretAccessKey: 'test', SessionToken: 'test', Expiration: new Date(8640000000000000) },
|
||||
true,
|
||||
@@ -77,22 +72,20 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
});
|
||||
|
||||
it('handles getBooleanInput correctly', {}, () => {
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('true');
|
||||
vi.mocked(core.getInput).mockReturnValue('true');
|
||||
expect(helpers.getBooleanInput('test')).toBe(true);
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('false');
|
||||
|
||||
vi.mocked(core.getInput).mockReturnValue('false');
|
||||
expect(helpers.getBooleanInput('test')).toBe(false);
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('');
|
||||
|
||||
vi.mocked(core.getInput).mockReturnValue('');
|
||||
expect(helpers.getBooleanInput('test', { default: true })).toBe(true);
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('invalid');
|
||||
|
||||
vi.mocked(core.getInput).mockReturnValue('invalid');
|
||||
expect(() => helpers.getBooleanInput('test')).toThrow();
|
||||
});
|
||||
|
||||
it('clears session token when not provided', {}, () => {
|
||||
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
||||
process.env.AWS_SESSION_TOKEN = 'old-token';
|
||||
helpers.exportCredentials({ AccessKeyId: 'test', SecretAccessKey: 'test' }, false, true);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
|
||||
|
||||
+77
-83
@@ -13,23 +13,17 @@ import { run } from '../src/index';
|
||||
import * as profileManager from '../src/profileManager';
|
||||
import mocks from './mockinputs.test';
|
||||
|
||||
vi.mock('@actions/core');
|
||||
vi.mock('node:fs');
|
||||
|
||||
const mockedSTSClient = mockClient(STSClient);
|
||||
|
||||
describe('Configure AWS Credentials', {}, () => {
|
||||
beforeEach(() => {
|
||||
// Reset mock state
|
||||
vi.restoreAllMocks();
|
||||
vi.resetAllMocks();
|
||||
mockedSTSClient.reset();
|
||||
// Mock GitHub Actions core functions
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation((_s) => {});
|
||||
vi.spyOn(core, 'setFailed').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'setOutput').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'debug').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'info').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'notice').mockImplementation((_m) => {});
|
||||
vi.mocked(core.getInput).mockReturnValue('');
|
||||
vi.mocked(core.getMultilineInput).mockReturnValue([]);
|
||||
// Remove any existing environment variables before each test to prevent the
|
||||
// SDK from picking them up
|
||||
process.env = { ...mocks.envs };
|
||||
@@ -38,8 +32,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
describe('GitHub OIDC Authentication', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
});
|
||||
@@ -91,7 +85,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('IAM User Authentication', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValueOnce({
|
||||
@@ -118,7 +112,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('AssumeRole with IAM LTC', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS));
|
||||
mockedSTSClient.on(AssumeRoleCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY }); // 3 times
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
@@ -152,7 +146,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('AssumeRole with WebIdentityTokeFile', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.WEBIDENTITY_TOKEN_FILE_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.WEBIDENTITY_TOKEN_FILE_INPUTS));
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
vol.reset();
|
||||
@@ -187,7 +181,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
process.env = { ...mocks.envs };
|
||||
});
|
||||
it('exports environment variables from env variables', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.EXISTING_ROLE_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.EXISTING_ROLE_INPUTS));
|
||||
process.env.AWS_ACCESS_KEY_ID = 'MYAWSACCESSKEYID';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'MYAWSSECRETACCESSKEY';
|
||||
process.env.AWS_SESSION_TOKEN = 'MYAWSSESSIONTOKEN';
|
||||
@@ -210,7 +204,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
expect(core.setFailed).not.toHaveBeenCalled();
|
||||
});
|
||||
it('exports environment variables from inputs', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.EXISTING_ROLE_INPUTS,
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
@@ -246,14 +240,14 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('Odd inputs', {}, () => {
|
||||
it('fails when github env vars are missing', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
delete process.env.GITHUB_REPOSITORY;
|
||||
delete process.env.GITHUB_SHA;
|
||||
await run();
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it('does not fail if GITHUB_REF is missing', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValueOnce({
|
||||
@@ -264,12 +258,12 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
expect(core.setFailed).not.toHaveBeenCalled();
|
||||
});
|
||||
it('fails with an invalid region', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({ 'aws-region': '$|<1B1D1 701L37' }));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput({ 'aws-region': '$|<1B1D1 701L37' }));
|
||||
await run();
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it('fails if access key id is provided without secret access key', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({ ...mocks.IAM_USER_INPUTS, 'aws-secret-access-key': '' }),
|
||||
);
|
||||
await run();
|
||||
@@ -277,14 +271,14 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
it('handles improper retry-max-attempts input', {}, async () => {
|
||||
// This should mean we retry one time
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'retry-max-attempts': '-1',
|
||||
'special-characters-workaround': 'false',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
mockedSTSClient
|
||||
@@ -296,27 +290,27 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it('fails if doing OIDC without the ACTIONS_ID_TOKEN_REQUEST_TOKEN', {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
delete process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
||||
await run();
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it("gets new creds if told to reuse existing but they're invalid", {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).rejects();
|
||||
await run();
|
||||
expect(core.notice).toHaveBeenCalledWith('No valid credentials exist. Running as normal.');
|
||||
});
|
||||
it("doesn't get new creds if there are already valid ones and we said use them", {}, async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves(mocks.outputs.GET_CALLER_IDENTITY);
|
||||
await run();
|
||||
expect(core.setFailed).not.toHaveBeenCalled();
|
||||
});
|
||||
it("doesn't export credentials as environment variables if told not to", {}, async () => {
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
await run();
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
||||
@@ -325,8 +319,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
it('can export creds as step outputs without exporting as env variables', {}, async () => {
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.STEP_BUT_NO_ENV_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.STEP_BUT_NO_ENV_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
await run();
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
||||
@@ -343,13 +337,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('skips OIDC when force-skip-oidc is true with IAM credentials', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'force-skip-oidc': 'true',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
@@ -364,13 +358,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('skips OIDC when force-skip-oidc is true with web identity token file', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.WEBIDENTITY_TOKEN_FILE_INPUTS,
|
||||
'force-skip-oidc': 'true',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -385,7 +379,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('fails when force-skip-oidc is true but no alternative credentials provided', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
@@ -401,13 +395,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('allows force-skip-oidc without role-to-assume', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'force-skip-oidc': 'true',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
@@ -424,13 +418,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('uses OIDC when force-skip-oidc is false (default behavior)', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'force-skip-oidc': 'false',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -442,8 +436,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('uses OIDC when force-skip-oidc is not set (default behavior)', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -455,7 +449,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('works with role chaining when force-skip-oidc is true', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.EXISTING_ROLE_INPUTS,
|
||||
'force-skip-oidc': 'true',
|
||||
@@ -463,7 +457,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
@@ -485,7 +479,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('succeeds when account ID matches allowed list', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '111111111111',
|
||||
@@ -503,7 +497,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('succeeds with multiple allowed account IDs when account matches', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '999999999999,111111111111,222222222222',
|
||||
@@ -520,7 +514,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('fails when account ID does not match allowed list', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '999999999999',
|
||||
@@ -539,7 +533,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('fails when account ID does not match any in multiple allowed accounts', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '999999999999,888888888888',
|
||||
@@ -559,7 +553,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('works with assume role when account ID matches', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'allowed-account-ids': '111111111111',
|
||||
@@ -578,13 +572,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('works with OIDC when account ID matches', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'allowed-account-ids': '111111111111',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -595,7 +589,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('handles GetCallerIdentity API failure gracefully', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '111111111111',
|
||||
@@ -612,7 +606,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('ignores validation when allowed-account-ids is empty', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': '',
|
||||
@@ -630,7 +624,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('handles whitespace in allowed-account-ids input', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'allowed-account-ids': ' 111111111111 , 222222222222 ',
|
||||
@@ -649,7 +643,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('Global Timeout Configuration', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValueOnce({
|
||||
@@ -660,8 +654,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
it('sets timeout when action-timeout-s is provided', async () => {
|
||||
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'action-timeout-s': '30',
|
||||
@@ -678,8 +672,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
it('does not set timeout when action-timeout-s is 0', async () => {
|
||||
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'action-timeout-s': '0',
|
||||
@@ -695,7 +689,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
it('does not set timeout when action-timeout-s is not provided', async () => {
|
||||
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
|
||||
await run();
|
||||
|
||||
@@ -707,7 +701,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
it('timeout callback calls setFailed and exits process', async () => {
|
||||
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||
const processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'action-timeout-s': '5',
|
||||
@@ -729,16 +723,16 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
|
||||
describe('HTTP Proxy Configuration', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
});
|
||||
|
||||
it('configures proxy from http-proxy input', async () => {
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'http-proxy': 'http://proxy.example.com:8080',
|
||||
@@ -752,7 +746,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('configures proxy from HTTP_PROXY environment variable', async () => {
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com:8080';
|
||||
|
||||
await run();
|
||||
@@ -762,7 +756,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('configures proxy from HTTPS_PROXY environment variable', async () => {
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
process.env.HTTPS_PROXY = 'https://proxy.example.com:8080';
|
||||
|
||||
await run();
|
||||
@@ -772,9 +766,9 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('prioritizes http-proxy input over environment variables', async () => {
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
process.env.HTTP_PROXY = 'http://env-proxy.example.com:8080';
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'http-proxy': 'http://input-proxy.example.com:8080',
|
||||
@@ -788,9 +782,9 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('properly configures proxy agent in STS client', async () => {
|
||||
const infoSpy = vi.spyOn(core, 'info');
|
||||
const infoSpy = vi.mocked(core.info);
|
||||
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'http-proxy': 'http://proxy.example.com:8080',
|
||||
@@ -804,7 +798,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('configures no-proxy setting', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'http-proxy': 'http://proxy.example.com:8080',
|
||||
@@ -832,13 +826,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('writes profile files with OIDC authentication', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'aws-profile': 'dev',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -861,7 +855,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('writes profile files with IAM user credentials', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_USER_INPUTS,
|
||||
'aws-profile': 'production',
|
||||
@@ -891,7 +885,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('writes profile files with IAM user role assumption', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'aws-profile': 'assumed-role',
|
||||
@@ -928,14 +922,14 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('respects output-env-credentials=true with profiles', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'aws-profile': 'dev',
|
||||
'output-env-credentials': 'true',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -958,8 +952,8 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('maintains backward compatibility when aws-profile is not specified', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -983,13 +977,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('handles default profile correctly', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'aws-profile': 'default',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
@@ -1004,13 +998,13 @@ describe('Configure AWS Credentials', {}, () => {
|
||||
});
|
||||
|
||||
it('rejects invalid profile names with whitespace', async () => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation(
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.GH_OIDC_INPUTS,
|
||||
'aws-profile': 'invalid profile',
|
||||
}),
|
||||
);
|
||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||
|
||||
@@ -11,14 +11,13 @@ import {
|
||||
writeProfileFiles,
|
||||
} from '../src/profileManager';
|
||||
|
||||
vi.mock('@actions/core');
|
||||
vi.mock('node:fs');
|
||||
|
||||
describe('Profile Manager', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.resetAllMocks();
|
||||
vol.reset();
|
||||
vi.spyOn(core, 'debug').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'info').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
describe('parseIni', {}, () => {
|
||||
|
||||
Reference in New Issue
Block a user