Renamed variables and added tests

pull/1006/head
Sankalp Kotewar 2 years ago committed by GitHub
parent d91f5bd2fd
commit 3d4af52c52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,7 @@
import * as cache from "@actions/cache"; import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, RefKey } from "../src/constants";
import run from "../src/restore"; import run from "../src/restore";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -45,158 +45,6 @@ afterEach(() => {
delete process.env[RefKey]; delete process.env[RefKey];
}); });
test("restore with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run();
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES with AC available ", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const path = "node_modules";
const key = "node-test";
testUtils.setInputs({
path: path,
key
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(key);
});
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore with no path should fail", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
// this input isn't necessary for restore b/c tarball contains entries relative to workspace
expect(failedMock).not.toHaveBeenCalledWith(
"Input required and not supplied: path"
);
});
test("restore with no key", async () => {
testUtils.setInput(Inputs.Path, "node_modules");
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledWith(
"Input required and not supplied: key"
);
});
test("restore with too many keys should fail", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKeys = [...Array(20).keys()].map(x => x.toString());
testUtils.setInputs({
path: path,
key,
restoreKeys
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, restoreKeys);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: Keys are limited to a maximum of 10.`
);
});
test("restore with large key should fail", async () => {
const path = "node_modules";
const key = "foo".repeat(512); // Over the 512 character limit
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot be larger than 512 characters.`
);
});
test("restore with invalid key should fail", async () => {
const path = "node_modules";
const key = "comma,comma";
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot contain commas.`
);
});
test("restore with no cache found", async () => { test("restore with no cache found", async () => {
const path = "node_modules"; const path = "node_modules";
const key = "node-test"; const key = "node-test";

@ -0,0 +1,326 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants";
import run from "../src/restoreImpl";
import { StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils";
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.isExactKeyMatch(key, cacheResult);
}
);
jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.isValidEvent();
});
jest.spyOn(actionUtils, "getInputAsArray").mockImplementation(
(name, options) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.getInputAsArray(name, options);
}
);
});
beforeEach(() => {
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => true
);
});
afterEach(() => {
testUtils.clearInputs();
delete process.env[Events.Key];
delete process.env[RefKey];
});
test("restore with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run(new StateProvider());
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES with AC available ", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const path = "node_modules";
const key = "node-test";
testUtils.setInputs({
path: path,
key
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(key);
});
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore with no path should fail", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
// this input isn't necessary for restore b/c tarball contains entries relative to workspace
expect(failedMock).not.toHaveBeenCalledWith(
"Input required and not supplied: path"
);
});
test("restore with no key", async () => {
testUtils.setInput(Inputs.Path, "node_modules");
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledWith(
"Input required and not supplied: key"
);
});
test("restore with too many keys should fail", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKeys = [...Array(20).keys()].map(x => x.toString());
testUtils.setInputs({
path: path,
key,
restoreKeys
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, restoreKeys);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: Keys are limited to a maximum of 10.`
);
});
test("restore with large key should fail", async () => {
const path = "node_modules";
const key = "foo".repeat(512); // Over the 512 character limit
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot be larger than 512 characters.`
);
});
test("restore with invalid key should fail", async () => {
const path = "node_modules";
const key = "comma,comma";
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot contain commas.`
);
});
test("restore with no cache found", async () => {
const path = "node_modules";
const key = "node-test";
testUtils.setInputs({
path: path,
key
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(undefined);
});
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith(
`Cache not found for input keys: ${key}`
);
});
test("restore with restore keys and no cache found", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKey = "node-";
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(undefined);
});
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith(
`Cache not found for input keys: ${key}, ${restoreKey}`
);
});
test("restore with cache found for key", async () => {
const path = "node_modules";
const key = "node-test";
testUtils.setInputs({
path: path,
key
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(key);
});
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore with cache found for restore key", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKey = "node-";
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(restoreKey);
});
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
expect(infoMock).toHaveBeenCalledWith(
`Cache restored from key: ${restoreKey}`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});

@ -1,8 +1,9 @@
import * as cache from "@actions/cache"; import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, RefKey } from "../src/constants";
import run from "../src/restoreOnly"; import run from "../src/restoreImpl";
import { StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -45,157 +46,6 @@ afterEach(() => {
delete process.env[RefKey]; delete process.env[RefKey];
}); });
test("restore with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run();
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const outputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(outputMock).toHaveBeenCalledTimes(1);
expect(outputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const outputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(outputMock).toHaveBeenCalledTimes(1);
expect(outputMock).toHaveBeenCalledWith(false);
});
test("restore on GHES with AC available ", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const path = "node_modules";
const key = "node-test";
testUtils.setInputs({
path: path,
key
});
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const outputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(key);
});
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(outputMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(outputMock).toHaveBeenCalledTimes(3);
expect(outputMock).toHaveBeenCalledWith("cache-hit", "true");
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("restore with no path should fail", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
// this input isn't necessary for restore b/c tarball contains entries relative to workspace
expect(failedMock).not.toHaveBeenCalledWith(
"Input required and not supplied: path"
);
});
test("restore with no key", async () => {
testUtils.setInput(Inputs.Path, "node_modules");
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledWith(
"Input required and not supplied: key"
);
});
test("restore with too many keys should fail", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKeys = [...Array(20).keys()].map(x => x.toString());
testUtils.setInputs({
path: path,
key,
restoreKeys
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, restoreKeys);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: Keys are limited to a maximum of 10.`
);
});
test("restore with large key should fail", async () => {
const path = "node_modules";
const key = "foo".repeat(512); // Over the 512 character limit
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot be larger than 512 characters.`
);
});
test("restore with invalid key should fail", async () => {
const path = "node_modules";
const key = "comma,comma";
testUtils.setInputs({
path: path,
key
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot contain commas.`
);
});
test("restore with no cache found", async () => { test("restore with no cache found", async () => {
const path = "node_modules"; const path = "node_modules";
const key = "node-test"; const key = "node-test";
@ -206,19 +56,19 @@ test("restore with no cache found", async () => {
const infoMock = jest.spyOn(core, "info"); const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const outputMock = jest.spyOn(core, "setOutput"); const stateMock = jest.spyOn(core, "saveState");
const restoreCacheMock = jest const restoreCacheMock = jest
.spyOn(cache, "restoreCache") .spyOn(cache, "restoreCache")
.mockImplementationOnce(() => { .mockImplementationOnce(() => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await run(); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []); expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(outputMock).toHaveBeenCalledWith("CACHE_KEY", key); expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith( expect(infoMock).toHaveBeenCalledWith(
@ -238,19 +88,19 @@ test("restore with restore keys and no cache found", async () => {
const infoMock = jest.spyOn(core, "info"); const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const outputMock = jest.spyOn(core, "setOutput"); const stateMock = jest.spyOn(core, "saveState");
const restoreCacheMock = jest const restoreCacheMock = jest
.spyOn(cache, "restoreCache") .spyOn(cache, "restoreCache")
.mockImplementationOnce(() => { .mockImplementationOnce(() => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await run(); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]); expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(outputMock).toHaveBeenCalledWith("CACHE_KEY", key); expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith( expect(infoMock).toHaveBeenCalledWith(
@ -268,21 +118,22 @@ test("restore with cache found for key", async () => {
const infoMock = jest.spyOn(core, "info"); const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const outputMock = jest.spyOn(core, "setOutput"); const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest const restoreCacheMock = jest
.spyOn(cache, "restoreCache") .spyOn(cache, "restoreCache")
.mockImplementationOnce(() => { .mockImplementationOnce(() => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await run(); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []); expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(outputMock).toHaveBeenCalledWith("CACHE_KEY", key); expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(outputMock).toHaveBeenCalledTimes(3); expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(outputMock).toHaveBeenCalledWith("cache-hit", "true"); expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
@ -300,21 +151,22 @@ test("restore with cache found for restore key", async () => {
const infoMock = jest.spyOn(core, "info"); const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const outputMock = jest.spyOn(core, "setOutput"); const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest const restoreCacheMock = jest
.spyOn(cache, "restoreCache") .spyOn(cache, "restoreCache")
.mockImplementationOnce(() => { .mockImplementationOnce(() => {
return Promise.resolve(restoreKey); return Promise.resolve(restoreKey);
}); });
await run(); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]); expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(outputMock).toHaveBeenCalledWith("CACHE_KEY", key); expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(outputMock).toHaveBeenCalledTimes(3); expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(outputMock).toHaveBeenCalledWith("cache-hit", "false"); expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
expect(infoMock).toHaveBeenCalledWith( expect(infoMock).toHaveBeenCalledWith(
`Cache restored from key: ${restoreKey}` `Cache restored from key: ${restoreKey}`
); );

@ -61,294 +61,6 @@ afterEach(() => {
delete process.env[RefKey]; delete process.env[RefKey];
}); });
test("save with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run();
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with no primary key in state outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const savedCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return "";
})
// Cache Key State
.mockImplementationOnce(() => {
return savedCacheKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
`Error retrieving key from state.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on ghes without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on GHES with AC available", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with exact match returns early", async () => {
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = primaryKey;
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with missing input outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
"Input required and not supplied: path"
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with large cache outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with reserve cache failure outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
const actualCache = jest.requireActual("@actions/cache");
const error = new actualCache.ReserveCacheError(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
throw error;
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledWith(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with server error outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error("HTTP Error Occurred");
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred");
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with valid inputs uploads a cache", async () => { test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");

@ -0,0 +1,388 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants";
import run from "../src/saveImpl";
import { StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils";
jest.mock("@actions/core");
jest.mock("@actions/cache");
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options);
});
jest.spyOn(actionUtils, "getInputAsArray").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsArray(name, options);
}
);
jest.spyOn(actionUtils, "getInputAsInt").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsInt(name, options);
}
);
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
return jest
.requireActual("../src/utils/actionUtils")
.isExactKeyMatch(key, cacheResult);
}
);
jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.isValidEvent();
});
});
beforeEach(() => {
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => true
);
});
afterEach(() => {
testUtils.clearInputs();
delete process.env[Events.Key];
delete process.env[RefKey];
});
test("save with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run(new StateProvider());
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with no primary key in state outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const savedCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return "";
})
// Cache Key State
.mockImplementationOnce(() => {
return savedCacheKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
`Error retrieving key from state.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on ghes without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on GHES with AC available", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with exact match returns early", async () => {
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = primaryKey;
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with missing input outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
"Input required and not supplied: path"
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with large cache outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
});
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with reserve cache failure outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
const actualCache = jest.requireActual("@actions/cache");
const error = new actualCache.ReserveCacheError(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
throw error;
});
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledWith(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with server error outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error("HTTP Error Occurred");
});
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred");
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(failedMock).toHaveBeenCalledTimes(0);
});

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, Inputs, RefKey } from "../src/constants";
import run from "../src/save"; import run from "../src/saveOnly";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -61,311 +61,13 @@ afterEach(() => {
delete process.env[RefKey]; delete process.env[RefKey];
}); });
test("save with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent;
delete process.env[RefKey];
await run();
expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with no primary key in state outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const savedCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return "";
})
// Cache Key State
.mockImplementationOnce(() => {
return savedCacheKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
`Error retrieving key from state.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on ghes without AC available should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => false
);
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
});
test("save on GHES with AC available", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with exact match returns early", async () => {
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = primaryKey;
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with missing input outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
"Input required and not supplied: path"
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with large cache outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with reserve cache failure outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
const actualCache = jest.requireActual("@actions/cache");
const error = new actualCache.ReserveCacheError(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
throw error;
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledWith(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with server error outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
throw new Error("HTTP Error Occurred");
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred");
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with valid inputs uploads a cache", async () => { test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules"; const inputPath = "node_modules";
testUtils.setInput(Inputs.Key, primaryKey);
testUtils.setInput(Inputs.Path, inputPath); testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000"); testUtils.setInput(Inputs.UploadChunkSize, "4000000");

@ -4943,20 +4943,19 @@ exports.checkBypass = checkBypass;
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; exports.stateToOutputMap = exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0;
var Inputs; var Inputs;
(function (Inputs) { (function (Inputs) {
Inputs["Key"] = "key"; Inputs["Key"] = "key";
Inputs["Path"] = "path"; Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys"; Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["RestoredKey"] = "restored-key"; // Input from save action
})(Inputs = exports.Inputs || (exports.Inputs = {})); })(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs; var Outputs;
(function (Outputs) { (function (Outputs) {
Outputs["CacheHit"] = "cache-hit"; Outputs["CacheHit"] = "cache-hit";
Outputs["InputtedKey"] = "inputted-key"; Outputs["CachePrimaryKey"] = "cache-primary-key";
Outputs["MatchedKey"] = "matched-key"; // Output from restore action Outputs["CacheRestoreKey"] = "cache-restore-key"; // Output from restore action
})(Outputs = exports.Outputs || (exports.Outputs = {})); })(Outputs = exports.Outputs || (exports.Outputs = {}));
var State; var State;
(function (State) { (function (State) {
@ -4970,6 +4969,10 @@ var Events;
Events["PullRequest"] = "pull_request"; Events["PullRequest"] = "pull_request";
})(Events = exports.Events || (exports.Events = {})); })(Events = exports.Events || (exports.Events = {}));
exports.RefKey = "GITHUB_REF"; exports.RefKey = "GITHUB_REF";
exports.stateToOutputMap = new Map([
[State.CacheMatchedKey, Outputs.CacheRestoreKey],
[State.CachePrimaryKey, Outputs.CachePrimaryKey]
]);
/***/ }), /***/ }),
@ -9361,7 +9364,7 @@ const constants_1 = __webpack_require__(196);
class StateProviderBase { class StateProviderBase {
constructor() { constructor() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
this.setState = (key, value, outputKey) => { }; this.setState = (key, value) => { };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
} }
@ -9385,10 +9388,8 @@ exports.StateProvider = StateProvider;
class NullStateProvider extends StateProviderBase { class NullStateProvider extends StateProviderBase {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.setState = (key, value, outputKey) => { this.setState = (key, value) => {
if (outputKey) { core.setOutput(constants_1.stateToOutputMap.get(key), value);
core.setOutput(outputKey, value);
}
}; };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
@ -50467,7 +50468,7 @@ function restoreImpl(stateProvider) {
return; return;
} }
const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true }); const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true });
stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey, constants_1.Outputs.InputtedKey); stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey);
const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys);
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, { const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true required: true
@ -50481,7 +50482,7 @@ function restoreImpl(stateProvider) {
return; return;
} }
// Store the matched cache key in states // Store the matched cache key in states
stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey, constants_1.Outputs.MatchedKey); stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey);
const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey); const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey);
core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString()); core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString());
core.info(`Cache restored from key: ${cacheKey}`); core.info(`Cache restored from key: ${cacheKey}`);

@ -4943,20 +4943,19 @@ exports.checkBypass = checkBypass;
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; exports.stateToOutputMap = exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0;
var Inputs; var Inputs;
(function (Inputs) { (function (Inputs) {
Inputs["Key"] = "key"; Inputs["Key"] = "key";
Inputs["Path"] = "path"; Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys"; Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["RestoredKey"] = "restored-key"; // Input from save action
})(Inputs = exports.Inputs || (exports.Inputs = {})); })(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs; var Outputs;
(function (Outputs) { (function (Outputs) {
Outputs["CacheHit"] = "cache-hit"; Outputs["CacheHit"] = "cache-hit";
Outputs["InputtedKey"] = "inputted-key"; Outputs["CachePrimaryKey"] = "cache-primary-key";
Outputs["MatchedKey"] = "matched-key"; // Output from restore action Outputs["CacheRestoreKey"] = "cache-restore-key"; // Output from restore action
})(Outputs = exports.Outputs || (exports.Outputs = {})); })(Outputs = exports.Outputs || (exports.Outputs = {}));
var State; var State;
(function (State) { (function (State) {
@ -4970,6 +4969,10 @@ var Events;
Events["PullRequest"] = "pull_request"; Events["PullRequest"] = "pull_request";
})(Events = exports.Events || (exports.Events = {})); })(Events = exports.Events || (exports.Events = {}));
exports.RefKey = "GITHUB_REF"; exports.RefKey = "GITHUB_REF";
exports.stateToOutputMap = new Map([
[State.CacheMatchedKey, Outputs.CacheRestoreKey],
[State.CachePrimaryKey, Outputs.CachePrimaryKey]
]);
/***/ }), /***/ }),
@ -9361,7 +9364,7 @@ const constants_1 = __webpack_require__(196);
class StateProviderBase { class StateProviderBase {
constructor() { constructor() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
this.setState = (key, value, outputKey) => { }; this.setState = (key, value) => { };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
} }
@ -9385,10 +9388,8 @@ exports.StateProvider = StateProvider;
class NullStateProvider extends StateProviderBase { class NullStateProvider extends StateProviderBase {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.setState = (key, value, outputKey) => { this.setState = (key, value) => {
if (outputKey) { core.setOutput(constants_1.stateToOutputMap.get(key), value);
core.setOutput(outputKey, value);
}
}; };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
@ -50467,7 +50468,7 @@ function restoreImpl(stateProvider) {
return; return;
} }
const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true }); const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true });
stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey, constants_1.Outputs.InputtedKey); stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey);
const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys);
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, { const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true required: true
@ -50481,7 +50482,7 @@ function restoreImpl(stateProvider) {
return; return;
} }
// Store the matched cache key in states // Store the matched cache key in states
stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey, constants_1.Outputs.MatchedKey); stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey);
const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey); const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey);
core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString()); core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString());
core.info(`Cache restored from key: ${cacheKey}`); core.info(`Cache restored from key: ${cacheKey}`);

@ -4972,20 +4972,19 @@ exports.checkBypass = checkBypass;
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; exports.stateToOutputMap = exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0;
var Inputs; var Inputs;
(function (Inputs) { (function (Inputs) {
Inputs["Key"] = "key"; Inputs["Key"] = "key";
Inputs["Path"] = "path"; Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys"; Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["RestoredKey"] = "restored-key"; // Input from save action
})(Inputs = exports.Inputs || (exports.Inputs = {})); })(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs; var Outputs;
(function (Outputs) { (function (Outputs) {
Outputs["CacheHit"] = "cache-hit"; Outputs["CacheHit"] = "cache-hit";
Outputs["InputtedKey"] = "inputted-key"; Outputs["CachePrimaryKey"] = "cache-primary-key";
Outputs["MatchedKey"] = "matched-key"; // Output from restore action Outputs["CacheRestoreKey"] = "cache-restore-key"; // Output from restore action
})(Outputs = exports.Outputs || (exports.Outputs = {})); })(Outputs = exports.Outputs || (exports.Outputs = {}));
var State; var State;
(function (State) { (function (State) {
@ -4999,6 +4998,10 @@ var Events;
Events["PullRequest"] = "pull_request"; Events["PullRequest"] = "pull_request";
})(Events = exports.Events || (exports.Events = {})); })(Events = exports.Events || (exports.Events = {}));
exports.RefKey = "GITHUB_REF"; exports.RefKey = "GITHUB_REF";
exports.stateToOutputMap = new Map([
[State.CacheMatchedKey, Outputs.CacheRestoreKey],
[State.CachePrimaryKey, Outputs.CachePrimaryKey]
]);
/***/ }), /***/ }),
@ -9390,7 +9393,7 @@ const constants_1 = __webpack_require__(196);
class StateProviderBase { class StateProviderBase {
constructor() { constructor() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
this.setState = (key, value, outputKey) => { }; this.setState = (key, value) => { };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
} }
@ -9414,10 +9417,8 @@ exports.StateProvider = StateProvider;
class NullStateProvider extends StateProviderBase { class NullStateProvider extends StateProviderBase {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.setState = (key, value, outputKey) => { this.setState = (key, value) => {
if (outputKey) { core.setOutput(constants_1.stateToOutputMap.get(key), value);
core.setOutput(outputKey, value);
}
}; };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
@ -41111,7 +41112,7 @@ function saveImpl(stateProvider) {
} }
// If matched restore key is same as primary key, then do not save cache // If matched restore key is same as primary key, then do not save cache
// NO-OP in case of SaveOnly action // NO-OP in case of SaveOnly action
const restoredKey = stateProvider.getCacheState() || core.getInput(constants_1.Inputs.RestoredKey); const restoredKey = stateProvider.getCacheState();
if (utils.isExactKeyMatch(primaryKey, restoredKey)) { if (utils.isExactKeyMatch(primaryKey, restoredKey)) {
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`); core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
return; return;

23
dist/save/index.js vendored

@ -4943,20 +4943,19 @@ exports.checkBypass = checkBypass;
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; exports.stateToOutputMap = exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0;
var Inputs; var Inputs;
(function (Inputs) { (function (Inputs) {
Inputs["Key"] = "key"; Inputs["Key"] = "key";
Inputs["Path"] = "path"; Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys"; Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["RestoredKey"] = "restored-key"; // Input from save action
})(Inputs = exports.Inputs || (exports.Inputs = {})); })(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs; var Outputs;
(function (Outputs) { (function (Outputs) {
Outputs["CacheHit"] = "cache-hit"; Outputs["CacheHit"] = "cache-hit";
Outputs["InputtedKey"] = "inputted-key"; Outputs["CachePrimaryKey"] = "cache-primary-key";
Outputs["MatchedKey"] = "matched-key"; // Output from restore action Outputs["CacheRestoreKey"] = "cache-restore-key"; // Output from restore action
})(Outputs = exports.Outputs || (exports.Outputs = {})); })(Outputs = exports.Outputs || (exports.Outputs = {}));
var State; var State;
(function (State) { (function (State) {
@ -4970,6 +4969,10 @@ var Events;
Events["PullRequest"] = "pull_request"; Events["PullRequest"] = "pull_request";
})(Events = exports.Events || (exports.Events = {})); })(Events = exports.Events || (exports.Events = {}));
exports.RefKey = "GITHUB_REF"; exports.RefKey = "GITHUB_REF";
exports.stateToOutputMap = new Map([
[State.CacheMatchedKey, Outputs.CacheRestoreKey],
[State.CachePrimaryKey, Outputs.CachePrimaryKey]
]);
/***/ }), /***/ }),
@ -9361,7 +9364,7 @@ const constants_1 = __webpack_require__(196);
class StateProviderBase { class StateProviderBase {
constructor() { constructor() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
this.setState = (key, value, outputKey) => { }; this.setState = (key, value) => { };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
} }
@ -9385,10 +9388,8 @@ exports.StateProvider = StateProvider;
class NullStateProvider extends StateProviderBase { class NullStateProvider extends StateProviderBase {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.setState = (key, value, outputKey) => { this.setState = (key, value) => {
if (outputKey) { core.setOutput(constants_1.stateToOutputMap.get(key), value);
core.setOutput(outputKey, value);
}
}; };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
this.getState = (key) => ""; this.getState = (key) => "";
@ -41082,7 +41083,7 @@ function saveImpl(stateProvider) {
} }
// If matched restore key is same as primary key, then do not save cache // If matched restore key is same as primary key, then do not save cache
// NO-OP in case of SaveOnly action // NO-OP in case of SaveOnly action
const restoredKey = stateProvider.getCacheState() || core.getInput(constants_1.Inputs.RestoredKey); const restoredKey = stateProvider.getCacheState();
if (utils.isExactKeyMatch(primaryKey, restoredKey)) { if (utils.isExactKeyMatch(primaryKey, restoredKey)) {
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`); core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
return; return;

@ -14,9 +14,9 @@ inputs:
outputs: outputs:
cache-hit: cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key' description: 'A boolean value to indicate an exact match was found for the primary key'
inputted-key: cache-primary-key:
description: 'Key passed in the input to use in subsequent steps of the workflow' description: 'Cache primary key passed in the input to use in subsequent steps of the workflow'
matched-key: cache-restore-key:
description: 'Cache key restored' description: 'Cache key restored'
runs: runs:
using: 'node16' using: 'node16'

@ -11,9 +11,6 @@ inputs:
upload-chunk-size: upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes' description: 'The chunk size used to split up large files during upload, in bytes'
required: false required: false
restored-key:
description: 'Cache key restored from the restore action'
required: false
runs: runs:
using: 'node16' using: 'node16'
main: '../dist/save-only/index.js' main: '../dist/save-only/index.js'

@ -1,15 +1,14 @@
export enum Inputs { export enum Inputs {
Key = "key", // Input from cache, restore, save action Key = "key", // Input for cache, restore, save action
Path = "path", // Input from cache, restore, save action Path = "path", // Input for cache, restore, save action
RestoreKeys = "restore-keys", // Input from cache, restore action RestoreKeys = "restore-keys", // Input for cache, restore action
UploadChunkSize = "upload-chunk-size", // Input from cache, save action UploadChunkSize = "upload-chunk-size" // Input for cache, save action
RestoredKey = "restored-key" // Input from save action
} }
export enum Outputs { export enum Outputs {
CacheHit = "cache-hit", // Output from cache, restore action CacheHit = "cache-hit", // Output from cache, restore action
InputtedKey = "inputted-key", // Output from restore action CachePrimaryKey = "cache-primary-key", // Output from restore action
MatchedKey = "matched-key" // Output from restore action CacheRestoreKey = "cache-restore-key" // Output from restore action
} }
export enum State { export enum State {
@ -24,3 +23,8 @@ export enum Events {
} }
export const RefKey = "GITHUB_REF"; export const RefKey = "GITHUB_REF";
export const stateToOutputMap = new Map<string, string>([
[State.CacheMatchedKey, Outputs.CacheRestoreKey],
[State.CachePrimaryKey, Outputs.CachePrimaryKey]
]);

@ -25,11 +25,7 @@ async function restoreImpl(
} }
const primaryKey = core.getInput(Inputs.Key, { required: true }); const primaryKey = core.getInput(Inputs.Key, { required: true });
stateProvider.setState( stateProvider.setState(State.CachePrimaryKey, primaryKey);
State.CachePrimaryKey,
primaryKey,
Outputs.InputtedKey
);
const restoreKeys = utils.getInputAsArray(Inputs.RestoreKeys); const restoreKeys = utils.getInputAsArray(Inputs.RestoreKeys);
const cachePaths = utils.getInputAsArray(Inputs.Path, { const cachePaths = utils.getInputAsArray(Inputs.Path, {
@ -54,11 +50,7 @@ async function restoreImpl(
} }
// Store the matched cache key in states // Store the matched cache key in states
stateProvider.setState( stateProvider.setState(State.CacheMatchedKey, cacheKey);
State.CacheMatchedKey,
cacheKey,
Outputs.MatchedKey
);
const isExactKeyMatch = utils.isExactKeyMatch( const isExactKeyMatch = utils.isExactKeyMatch(
core.getInput(Inputs.Key, { required: true }), core.getInput(Inputs.Key, { required: true }),

@ -42,8 +42,7 @@ async function saveImpl(stateProvider: IStateProvider): Promise<void> {
// If matched restore key is same as primary key, then do not save cache // If matched restore key is same as primary key, then do not save cache
// NO-OP in case of SaveOnly action // NO-OP in case of SaveOnly action
const restoredKey = const restoredKey = stateProvider.getCacheState();
stateProvider.getCacheState() || core.getInput(Inputs.RestoredKey);
if (utils.isExactKeyMatch(primaryKey, restoredKey)) { if (utils.isExactKeyMatch(primaryKey, restoredKey)) {
core.info( core.info(

@ -1,9 +1,9 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import { State } from "./constants"; import { State, stateToOutputMap } from "./constants";
export interface IStateProvider { export interface IStateProvider {
setState(key: string, value: string, outputKey?: string): void; setState(key: string, value: string): void;
getState(key: string): string; getState(key: string): string;
getCacheState(): string | undefined; getCacheState(): string | undefined;
@ -21,7 +21,7 @@ class StateProviderBase implements IStateProvider {
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
setState = (key: string, value: string, outputKey?: string) => {}; setState = (key: string, value: string) => {};
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
getState = (key: string) => ""; getState = (key: string) => "";
@ -33,10 +33,8 @@ export class StateProvider extends StateProviderBase {
} }
export class NullStateProvider extends StateProviderBase { export class NullStateProvider extends StateProviderBase {
setState = (key: string, value: string, outputKey?: string) => { setState = (key: string, value: string) => {
if (outputKey) { core.setOutput(stateToOutputMap.get(key) as string, value);
core.setOutput(outputKey, value);
}
}; };
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
getState = (key: string) => ""; getState = (key: string) => "";

Loading…
Cancel
Save