mirror of
https://github.com/actions/cache.git
synced 2026-05-21 21:46:12 +08:00
Update packages, migrate to ESM
This commit is contained in:
@@ -1,25 +1,69 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterAll } from "@jest/globals";
|
||||
|
||||
import { Events, RefKey } from "../src/constants";
|
||||
import * as actionUtils from "../src/utils/actionUtils";
|
||||
import * as testUtils from "../src/utils/testUtils";
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
jest.mock("@actions/core");
|
||||
jest.mock("@actions/cache");
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, RefKey } = await import("../src/constants");
|
||||
const actionUtils = await import("../src/utils/actionUtils");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
let pristineEnv: NodeJS.ProcessEnv;
|
||||
|
||||
beforeAll(() => {
|
||||
pristineEnv = process.env;
|
||||
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
|
||||
return jest.requireActual("@actions/core").getInput(name, options);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = pristineEnv;
|
||||
pristineEnv = { ...process.env };
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
delete process.env[Events.Key];
|
||||
delete process.env[RefKey];
|
||||
});
|
||||
@@ -47,74 +91,44 @@ test("isGhes returns false when server url is github.com", () => {
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with undefined cache key returns false", () => {
|
||||
const key = "linux-rust";
|
||||
const cacheKey = undefined;
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false);
|
||||
expect(actionUtils.isExactKeyMatch("linux-rust", undefined)).toBe(false);
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with empty cache key returns false", () => {
|
||||
const key = "linux-rust";
|
||||
const cacheKey = "";
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false);
|
||||
expect(actionUtils.isExactKeyMatch("linux-rust", "")).toBe(false);
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with different keys returns false", () => {
|
||||
const key = "linux-rust";
|
||||
const cacheKey = "linux-";
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false);
|
||||
expect(actionUtils.isExactKeyMatch("linux-rust", "linux-")).toBe(false);
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with different key accents returns false", () => {
|
||||
const key = "linux-áccent";
|
||||
const cacheKey = "linux-accent";
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false);
|
||||
expect(actionUtils.isExactKeyMatch("linux-áccent", "linux-accent")).toBe(false);
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with same key returns true", () => {
|
||||
const key = "linux-rust";
|
||||
const cacheKey = "linux-rust";
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(true);
|
||||
expect(actionUtils.isExactKeyMatch("linux-rust", "linux-rust")).toBe(true);
|
||||
});
|
||||
|
||||
test("isExactKeyMatch with same key and different casing returns true", () => {
|
||||
const key = "linux-rust";
|
||||
const cacheKey = "LINUX-RUST";
|
||||
|
||||
expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(true);
|
||||
expect(actionUtils.isExactKeyMatch("linux-rust", "LINUX-RUST")).toBe(true);
|
||||
});
|
||||
|
||||
test("logWarning logs a message with a warning prefix", () => {
|
||||
const message = "A warning occurred.";
|
||||
|
||||
const infoMock = jest.spyOn(core, "info");
|
||||
|
||||
actionUtils.logWarning(message);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
expect(core.info).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
});
|
||||
|
||||
test("isValidEvent returns false for event that does not have a branch or tag", () => {
|
||||
const event = "foo";
|
||||
process.env[Events.Key] = event;
|
||||
|
||||
const isValidEvent = actionUtils.isValidEvent();
|
||||
|
||||
expect(isValidEvent).toBe(false);
|
||||
process.env[Events.Key] = "foo";
|
||||
expect(actionUtils.isValidEvent()).toBe(false);
|
||||
});
|
||||
|
||||
test("isValidEvent returns true for event that has a ref", () => {
|
||||
const event = Events.Push;
|
||||
process.env[Events.Key] = event;
|
||||
process.env[Events.Key] = Events.Push;
|
||||
process.env[RefKey] = "ref/heads/feature";
|
||||
|
||||
const isValidEvent = actionUtils.isValidEvent();
|
||||
|
||||
expect(isValidEvent).toBe(true);
|
||||
expect(actionUtils.isValidEvent()).toBe(true);
|
||||
});
|
||||
|
||||
test("getInputAsArray returns empty array if not required and missing", () => {
|
||||
@@ -124,7 +138,7 @@ test("getInputAsArray returns empty array if not required and missing", () => {
|
||||
test("getInputAsArray throws error if required and missing", () => {
|
||||
expect(() =>
|
||||
actionUtils.getInputAsArray("foo", { required: true })
|
||||
).toThrowError();
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test("getInputAsArray handles single line correctly", () => {
|
||||
@@ -180,7 +194,7 @@ test("getInputAsInt returns undefined if input is invalid or NaN", () => {
|
||||
test("getInputAsInt throws if required and value missing", () => {
|
||||
expect(() =>
|
||||
actionUtils.getInputAsInt("undefined", { required: true })
|
||||
).toThrowError();
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test("getInputAsBool returns false if input not set", () => {
|
||||
@@ -200,68 +214,65 @@ test("getInputAsBool returns false if input is invalid or NaN", () => {
|
||||
test("getInputAsBool throws if required and value missing", () => {
|
||||
expect(() =>
|
||||
actionUtils.getInputAsBool("undefined2", { required: true })
|
||||
).toThrowError();
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test("isCacheFeatureAvailable for ac enabled", () => {
|
||||
jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => true);
|
||||
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
expect(actionUtils.isCacheFeatureAvailable()).toBe(true);
|
||||
});
|
||||
|
||||
test("isCacheFeatureAvailable for ac disabled on GHES", () => {
|
||||
jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => false);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const message = `Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.
|
||||
Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`;
|
||||
const infoMock = jest.spyOn(core, "info");
|
||||
|
||||
try {
|
||||
process.env["GITHUB_SERVER_URL"] = "http://example.com";
|
||||
expect(actionUtils.isCacheFeatureAvailable()).toBe(false);
|
||||
expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
expect(core.info).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
} finally {
|
||||
delete process.env["GITHUB_SERVER_URL"];
|
||||
}
|
||||
});
|
||||
|
||||
test("isCacheFeatureAvailable for ac disabled on dotcom", () => {
|
||||
jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => false);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const message =
|
||||
"An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions.";
|
||||
const infoMock = jest.spyOn(core, "info");
|
||||
|
||||
try {
|
||||
process.env["GITHUB_SERVER_URL"] = "http://github.com";
|
||||
expect(actionUtils.isCacheFeatureAvailable()).toBe(false);
|
||||
expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
expect(core.info).toHaveBeenCalledWith(`[warning]${message}`);
|
||||
} finally {
|
||||
delete process.env["GITHUB_SERVER_URL"];
|
||||
}
|
||||
});
|
||||
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is not defined", async () => {
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is not defined", () => {
|
||||
delete process.env["GITHUB_SERVER_URL"];
|
||||
expect(actionUtils.isGhes()).toBeFalsy();
|
||||
});
|
||||
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to github.com", async () => {
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to github.com", () => {
|
||||
process.env["GITHUB_SERVER_URL"] = "https://github.com";
|
||||
expect(actionUtils.isGhes()).toBeFalsy();
|
||||
});
|
||||
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL", async () => {
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL", () => {
|
||||
process.env["GITHUB_SERVER_URL"] = "https://contoso.ghe.com";
|
||||
expect(actionUtils.isGhes()).toBeFalsy();
|
||||
});
|
||||
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix", async () => {
|
||||
test("isGhes returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix", () => {
|
||||
process.env["GITHUB_SERVER_URL"] = "https://mock-github.localhost";
|
||||
expect(actionUtils.isGhes()).toBeFalsy();
|
||||
});
|
||||
|
||||
test("isGhes returns true when the GITHUB_SERVER_URL environment variable is set to some other URL", async () => {
|
||||
test("isGhes returns true when the GITHUB_SERVER_URL environment variable is set to some other URL", () => {
|
||||
process.env["GITHUB_SERVER_URL"] = "https://src.onpremise.fabrikam.com";
|
||||
expect(actionUtils.isGhes()).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -1,50 +1,69 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, RefKey } from "../src/constants";
|
||||
import { restoreRun } from "../src/restoreImpl";
|
||||
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);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
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);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
const actualUtils = jest.requireActual("../src/utils/actionUtils");
|
||||
return actualUtils.getInputAsBool(name, options);
|
||||
}
|
||||
);
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, RefKey } = await import("../src/constants");
|
||||
const { restoreRun } = await import("../src/restoreImpl");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -62,19 +81,12 @@ test("restore with no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
@@ -84,12 +96,10 @@ test("restore with no cache found", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}`
|
||||
);
|
||||
});
|
||||
@@ -105,19 +115,12 @@ test("restore with restore keys and no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
@@ -127,12 +130,10 @@ test("restore with restore keys and no cache found", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}, ${restoreKey}`
|
||||
);
|
||||
});
|
||||
@@ -146,20 +147,12 @@ test("restore with cache found for key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(key);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
@@ -169,15 +162,13 @@ test("restore with cache found for key", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(2);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(core.info).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with cache found for restore key", async () => {
|
||||
@@ -191,39 +182,20 @@ test("restore with cache found for restore key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(restoreKey);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(2);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("Fail restore when fail on cache miss is enabled and primary + restore keys not found", async () => {
|
||||
@@ -237,35 +209,17 @@ test("Fail restore when fail on cache miss is enabled and primary + restore keys
|
||||
failOnCacheMiss: true
|
||||
});
|
||||
|
||||
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(undefined);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("restore when fail on cache miss is enabled and primary key doesn't match restored key", async () => {
|
||||
@@ -279,40 +233,20 @@ test("restore when fail on cache miss is enabled and primary key doesn't match r
|
||||
failOnCacheMiss: true
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(restoreKey);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(2);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with fail on cache miss disabled and no cache found", async () => {
|
||||
@@ -326,33 +260,15 @@ test("restore with fail on cache miss disabled and no cache found", async () =>
|
||||
failOnCacheMiss: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(1);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}, ${restoreKey}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -1,51 +1,71 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, Inputs, RefKey } from "../src/constants";
|
||||
import { restoreImpl } 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);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
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);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
const actualUtils = jest.requireActual("../src/utils/actionUtils");
|
||||
return actualUtils.getInputAsBool(name, options);
|
||||
}
|
||||
);
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, Inputs, RefKey } = await import("../src/constants");
|
||||
const { restoreImpl } = await import("../src/restoreImpl");
|
||||
const { StateProvider } = await import("../src/stateProvider");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(core.getState as jest.Mock).mockReturnValue("");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -55,52 +75,37 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
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 restoreImpl(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(core.info).toHaveBeenCalledWith(
|
||||
`[warning]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);
|
||||
expect(core.setFailed).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(core, "setOutput");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "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(core, "setOutput");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "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({
|
||||
@@ -109,20 +114,12 @@ test("restore on GHES with AC available ", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(key);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
@@ -132,32 +129,26 @@ test("restore on GHES with AC available ", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
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);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(core.info).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with no path should fail", async () => {
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
await restoreImpl(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(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).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 restoreImpl(new StateProvider());
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"Input required and not supplied: key"
|
||||
);
|
||||
});
|
||||
@@ -172,46 +163,30 @@ test("restore with too many keys should fail", async () => {
|
||||
restoreKeys,
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
restoreKeys,
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
(cache.restoreCache as jest.Mock).mockRejectedValue(
|
||||
new Error("Key Validation Error: Keys are limited to a maximum of 10.")
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).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
|
||||
const key = "foo".repeat(512);
|
||||
testUtils.setInputs({
|
||||
path: path,
|
||||
key,
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
(cache.restoreCache as jest.Mock).mockRejectedValue(
|
||||
new Error(`Key Validation Error: ${key} cannot be larger than 512 characters.`)
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`Key Validation Error: ${key} cannot be larger than 512 characters.`
|
||||
);
|
||||
});
|
||||
@@ -224,20 +199,12 @@ test("restore with invalid key should fail", async () => {
|
||||
key,
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
(cache.restoreCache as jest.Mock).mockRejectedValue(
|
||||
new Error(`Key Validation Error: ${key} cannot contain commas.`)
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
await restoreImpl(new StateProvider());
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`Key Validation Error: ${key} cannot contain commas.`
|
||||
);
|
||||
});
|
||||
@@ -251,32 +218,14 @@ test("restore with no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}`
|
||||
);
|
||||
});
|
||||
@@ -292,32 +241,14 @@ test("restore with restore keys and no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}, ${restoreKey}`
|
||||
);
|
||||
});
|
||||
@@ -331,35 +262,16 @@ test("restore with cache found for key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(key);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
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);
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(core.info).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with cache found for restore key", async () => {
|
||||
@@ -373,36 +285,18 @@ test("restore with cache found for restore key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(restoreKey);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with lookup-only set", async () => {
|
||||
@@ -414,20 +308,12 @@ test("restore with lookup-only set", async () => {
|
||||
lookupOnly: true
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(key);
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
@@ -437,31 +323,27 @@ test("restore with lookup-only set", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(core.saveState).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(2);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache found and can be restored from key: ${key}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore failure with earlyExit should call process exit", async () => {
|
||||
testUtils.setInput(Inputs.Path, "node_modules");
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
const processExitMock = jest.spyOn(process, "exit").mockImplementation();
|
||||
const processExitMock = jest.spyOn(process, "exit").mockImplementation((() => {}) as any);
|
||||
|
||||
// call restoreImpl with `earlyExit` set to true
|
||||
await restoreImpl(new StateProvider(), true);
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"Input required and not supplied: key"
|
||||
);
|
||||
expect(processExitMock).toHaveBeenCalledWith(1);
|
||||
processExitMock.mockRestore();
|
||||
});
|
||||
|
||||
@@ -1,51 +1,69 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, RefKey } from "../src/constants";
|
||||
import { restoreOnlyRun } from "../src/restoreImpl";
|
||||
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);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
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);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsBool(name, options);
|
||||
}
|
||||
);
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, RefKey } = await import("../src/constants");
|
||||
const { restoreOnlyRun } = await import("../src/restoreImpl");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -63,19 +81,12 @@ test("restore with no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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(undefined);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreOnlyRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.restoreCache).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
@@ -85,11 +96,10 @@ test("restore with no cache found", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledTimes(1);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(1);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}`
|
||||
);
|
||||
});
|
||||
@@ -105,32 +115,14 @@ test("restore with restore keys and no cache found", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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(undefined);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
await restoreOnlyRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache not found for input keys: ${key}, ${restoreKey}`
|
||||
);
|
||||
});
|
||||
@@ -144,36 +136,17 @@ test("restore with cache found for key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(key);
|
||||
|
||||
await restoreOnlyRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-matched-key", key);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledTimes(3);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-matched-key", key);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(3);
|
||||
expect(core.info).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with cache found for restore key", async () => {
|
||||
@@ -187,36 +160,17 @@ test("restore with cache found for restore key", async () => {
|
||||
enableCrossOsArchive: false
|
||||
});
|
||||
|
||||
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(restoreKey);
|
||||
});
|
||||
(cache.restoreCache as jest.Mock).mockResolvedValue(restoreKey);
|
||||
|
||||
await restoreOnlyRun();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[restoreKey],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-matched-key", restoreKey);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledTimes(3);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.restoreCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(core.setOutput).toHaveBeenCalledWith("cache-matched-key", restoreKey);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(3);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -1,70 +1,69 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, Inputs, RefKey } from "../src/constants";
|
||||
import { saveRun } from "../src/saveImpl";
|
||||
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(core, "getState").mockImplementation(name => {
|
||||
return jest.requireActual("@actions/core").getState(name);
|
||||
});
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsArray").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsArray(name, options);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsInt").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsInt(name, options);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsBool(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();
|
||||
});
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, Inputs, RefKey } = await import("../src/constants");
|
||||
const { saveRun } = await import("../src/saveImpl");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -74,36 +73,24 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
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 primaryKey;
|
||||
})
|
||||
// Cache Key State
|
||||
.mockImplementationOnce(() => {
|
||||
return savedCacheKey;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(primaryKey)
|
||||
.mockReturnValueOnce(savedCacheKey);
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockResolvedValue(cacheId);
|
||||
|
||||
await saveRun();
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.saveCache).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
{
|
||||
@@ -112,5 +99,5 @@ test("save with valid inputs uploads a cache", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -1,68 +1,71 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, Inputs, RefKey } from "../src/constants";
|
||||
import { saveImpl } 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);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsInt").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsInt(name, options);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsBool(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();
|
||||
});
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, Inputs, RefKey } = await import("../src/constants");
|
||||
const { saveImpl } = await import("../src/saveImpl");
|
||||
const { StateProvider } = await import("../src/stateProvider");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(core.getState as jest.Mock).mockReturnValue("");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -72,99 +75,61 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
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 saveImpl(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(core.info).toHaveBeenCalledWith(
|
||||
`[warning]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);
|
||||
expect(core.setFailed).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");
|
||||
(core.getState as jest.Mock).mockReturnValue("");
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(logWarningMock).toHaveBeenCalledWith(`Key is not specified.`);
|
||||
expect(logWarningMock).toHaveBeenCalledTimes(1);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(`[warning]Key is not specified.`);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("save without AC available should no-op", async () => {
|
||||
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
|
||||
() => false
|
||||
);
|
||||
|
||||
const saveCacheMock = jest.spyOn(cache, "saveCache");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(cache.saveCache).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");
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(false);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(cache.saveCache).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;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(primaryKey)
|
||||
.mockReturnValueOnce(savedCacheKey);
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockResolvedValue(cacheId);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.saveCache).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
{
|
||||
@@ -173,229 +138,135 @@ test("save on GHES with AC available", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).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");
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(primaryKey)
|
||||
.mockReturnValueOnce(primaryKey);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).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");
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(savedCacheKey)
|
||||
.mockReturnValueOnce(primaryKey);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(logWarningMock).toHaveBeenCalledWith(
|
||||
"Input required and not supplied: path"
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(0);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[warning]Input required and not supplied: path"
|
||||
);
|
||||
expect(logWarningMock).toHaveBeenCalledTimes(1);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).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;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(savedCacheKey)
|
||||
.mockReturnValueOnce(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."
|
||||
);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockRejectedValue(
|
||||
new Error(
|
||||
"Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
|
||||
)
|
||||
);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
expect.anything(),
|
||||
false
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[warning]Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache."
|
||||
);
|
||||
|
||||
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);
|
||||
expect(core.setFailed).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;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(savedCacheKey)
|
||||
.mockReturnValueOnce(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;
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockRejectedValue(
|
||||
new Error(
|
||||
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
|
||||
)
|
||||
);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
expect.anything(),
|
||||
false
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`[warning]Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
|
||||
);
|
||||
|
||||
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);
|
||||
expect(core.setFailed).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;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(savedCacheKey)
|
||||
.mockReturnValueOnce(primaryKey);
|
||||
|
||||
const inputPath = "node_modules";
|
||||
testUtils.setInput(Inputs.Path, inputPath);
|
||||
|
||||
const saveCacheMock = jest
|
||||
.spyOn(cache, "saveCache")
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error("HTTP Error Occurred");
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockRejectedValue(
|
||||
new Error("HTTP Error Occurred")
|
||||
);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
expect.anything(),
|
||||
false
|
||||
);
|
||||
|
||||
expect(logWarningMock).toHaveBeenCalledTimes(1);
|
||||
expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred");
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(core.info).toHaveBeenCalledWith("[warning]HTTP Error Occurred");
|
||||
expect(core.setFailed).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;
|
||||
});
|
||||
(core.getState as jest.Mock)
|
||||
.mockReturnValueOnce(primaryKey)
|
||||
.mockReturnValueOnce(savedCacheKey);
|
||||
|
||||
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);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockResolvedValue(cacheId);
|
||||
|
||||
await saveImpl(new StateProvider());
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.saveCache).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
{
|
||||
@@ -404,5 +275,5 @@ test("save with valid inputs uploads a cache", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -1,70 +1,69 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, Inputs, RefKey } from "../src/constants";
|
||||
import { saveOnlyRun } from "../src/saveImpl";
|
||||
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(core, "setOutput").mockImplementation((key, value) => {
|
||||
return jest.requireActual("@actions/core").getInput(key, value);
|
||||
});
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsArray").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsArray(name, options);
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
);
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsInt").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsInt(name, options);
|
||||
// Mock @actions/cache
|
||||
jest.unstable_mockModule("@actions/cache", () => ({
|
||||
restoreCache: jest.fn(),
|
||||
saveCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(() => true),
|
||||
ReserveCacheError: class ReserveCacheError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ReserveCacheError";
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsBool(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();
|
||||
});
|
||||
});
|
||||
const core = await import("@actions/core");
|
||||
const cache = await import("@actions/cache");
|
||||
const { Events, Inputs, RefKey } = await import("../src/constants");
|
||||
const { saveOnlyRun } = await import("../src/saveImpl");
|
||||
const testUtils = await import("../src/utils/testUtils");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(core.getInput as jest.Mock).mockImplementation(
|
||||
(name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[
|
||||
`INPUT_${name.replace(/ /g, "_").toUpperCase()}`
|
||||
] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(
|
||||
`Input required and not supplied: ${name}`
|
||||
);
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
);
|
||||
(cache.isFeatureAvailable as jest.Mock).mockReturnValue(true);
|
||||
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(() => {
|
||||
@@ -74,8 +73,6 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
test("save with valid inputs uploads a cache", async () => {
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
|
||||
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
|
||||
|
||||
const inputPath = "node_modules";
|
||||
@@ -84,16 +81,12 @@ test("save with valid inputs uploads a cache", async () => {
|
||||
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
|
||||
|
||||
const cacheId = 4;
|
||||
const saveCacheMock = jest
|
||||
.spyOn(cache, "saveCache")
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(cacheId);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockResolvedValue(cacheId);
|
||||
|
||||
await saveOnlyRun();
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.saveCache).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
{
|
||||
@@ -102,12 +95,10 @@ test("save with valid inputs uploads a cache", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.setFailed).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("save failing logs the warning message", async () => {
|
||||
const warningMock = jest.spyOn(core, "warning");
|
||||
|
||||
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
|
||||
|
||||
const inputPath = "node_modules";
|
||||
@@ -116,16 +107,12 @@ test("save failing logs the warning message", async () => {
|
||||
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
|
||||
|
||||
const cacheId = -1;
|
||||
const saveCacheMock = jest
|
||||
.spyOn(cache, "saveCache")
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(cacheId);
|
||||
});
|
||||
(cache.saveCache as jest.Mock).mockResolvedValue(cacheId);
|
||||
|
||||
await saveOnlyRun();
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(saveCacheMock).toHaveBeenCalledWith(
|
||||
expect(cache.saveCache).toHaveBeenCalledTimes(1);
|
||||
expect(cache.saveCache).toHaveBeenCalledWith(
|
||||
[inputPath],
|
||||
primaryKey,
|
||||
{
|
||||
@@ -134,6 +121,6 @@ test("save failing logs the warning message", async () => {
|
||||
false
|
||||
);
|
||||
|
||||
expect(warningMock).toHaveBeenCalledTimes(1);
|
||||
expect(warningMock).toHaveBeenCalledWith("Cache save failed.");
|
||||
expect(core.warning).toHaveBeenCalledTimes(1);
|
||||
expect(core.warning).toHaveBeenCalledWith("Cache save failed.");
|
||||
});
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import * as core from "@actions/core";
|
||||
import { jest, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
|
||||
import { Events, RefKey, State } from "../src/constants";
|
||||
import {
|
||||
IStateProvider,
|
||||
NullStateProvider,
|
||||
StateProvider
|
||||
} from "../src/stateProvider";
|
||||
// Mock @actions/core
|
||||
jest.unstable_mockModule("@actions/core", () => ({
|
||||
getInput: jest.fn((name: string, options?: { required?: boolean }) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
return val.trim();
|
||||
}),
|
||||
setOutput: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(() => ""),
|
||||
isDebug: jest.fn(() => false),
|
||||
exportVariable: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
jest.mock("@actions/core");
|
||||
const core = await import("@actions/core");
|
||||
const { Events, RefKey, State } = await import("../src/constants");
|
||||
const { NullStateProvider, StateProvider } = await import("../src/stateProvider");
|
||||
import type { IStateProvider } from "../src/stateProvider";
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
|
||||
return jest.requireActual("@actions/core").getInput(name, options);
|
||||
});
|
||||
|
||||
jest.spyOn(core, "setOutput").mockImplementation((key, value) => {
|
||||
return jest.requireActual("@actions/core").setOutput(key, value);
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(core.getState as jest.Mock).mockReturnValue("");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -26,21 +43,10 @@ afterEach(() => {
|
||||
|
||||
test("StateProvider saves states", async () => {
|
||||
const states = new Map<string, string>();
|
||||
const getStateMock = jest
|
||||
.spyOn(core, "getState")
|
||||
.mockImplementation(key => states.get(key) || "");
|
||||
|
||||
const saveStateMock = jest
|
||||
.spyOn(core, "saveState")
|
||||
.mockImplementation((key, value) => {
|
||||
states.set(key, value);
|
||||
});
|
||||
|
||||
const setOutputMock = jest
|
||||
.spyOn(core, "setOutput")
|
||||
.mockImplementation((key, value) => {
|
||||
return jest.requireActual("@actions/core").setOutput(key, value);
|
||||
});
|
||||
(core.getState as jest.Mock).mockImplementation((key: string) => states.get(key) || "");
|
||||
(core.saveState as jest.Mock).mockImplementation((key: string, value: string) => {
|
||||
states.set(key, value);
|
||||
});
|
||||
|
||||
const cacheMatchedKey = "node-cache";
|
||||
|
||||
@@ -52,38 +58,19 @@ test("StateProvider saves states", async () => {
|
||||
|
||||
expect(stateValue).toBe("stateValue");
|
||||
expect(cacheStateValue).toBe(cacheMatchedKey);
|
||||
expect(getStateMock).toHaveBeenCalledTimes(2);
|
||||
expect(saveStateMock).toHaveBeenCalledTimes(2);
|
||||
expect(setOutputMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.getState).toHaveBeenCalledTimes(2);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(2);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("NullStateProvider saves outputs", async () => {
|
||||
const getStateMock = jest
|
||||
.spyOn(core, "getState")
|
||||
.mockImplementation(name =>
|
||||
jest.requireActual("@actions/core").getState(name)
|
||||
);
|
||||
|
||||
const setOutputMock = jest
|
||||
.spyOn(core, "setOutput")
|
||||
.mockImplementation((key, value) => {
|
||||
return jest.requireActual("@actions/core").setOutput(key, value);
|
||||
});
|
||||
|
||||
const saveStateMock = jest
|
||||
.spyOn(core, "saveState")
|
||||
.mockImplementation((key, value) => {
|
||||
return jest.requireActual("@actions/core").saveState(key, value);
|
||||
});
|
||||
|
||||
const cacheMatchedKey = "node-cache";
|
||||
const nullStateProvider: IStateProvider = new NullStateProvider();
|
||||
nullStateProvider.setState(State.CacheMatchedKey, "outputValue");
|
||||
nullStateProvider.setState(State.CachePrimaryKey, cacheMatchedKey);
|
||||
nullStateProvider.setState(State.CachePrimaryKey, "node-cache");
|
||||
nullStateProvider.getState("outputKey");
|
||||
nullStateProvider.getCacheState();
|
||||
|
||||
expect(getStateMock).toHaveBeenCalledTimes(0);
|
||||
expect(setOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(saveStateMock).toHaveBeenCalledTimes(0);
|
||||
expect(core.getState).toHaveBeenCalledTimes(0);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(2);
|
||||
expect(core.saveState).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
require("nock").disableNetConnect();
|
||||
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ["js", "ts"],
|
||||
testEnvironment: "node",
|
||||
testMatch: ["**/*.test.ts"],
|
||||
testRunner: "jest-circus/runner",
|
||||
transform: {
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
verbose: true
|
||||
};
|
||||
|
||||
const processStdoutWrite = process.stdout.write.bind(process.stdout);
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
process.stdout.write = (str, encoding, cb) => {
|
||||
// Core library will directly call process.stdout.write for commands
|
||||
// We don't want :: commands to be executed by the runner during tests
|
||||
if (!String(str).match(/^::/)) {
|
||||
return processStdoutWrite(str, encoding, cb);
|
||||
}
|
||||
};
|
||||
21
jest.config.ts
Normal file
21
jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
roots: ['<rootDir>'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.ts$': [
|
||||
'ts-jest',
|
||||
{
|
||||
useESM: true,
|
||||
diagnostics: {
|
||||
ignoreCodes: [151002]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
|
||||
verbose: true
|
||||
}
|
||||
2546
package-lock.json
generated
2546
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "cache",
|
||||
"version": "5.0.4",
|
||||
"version": "6.0.0",
|
||||
"private": true,
|
||||
"description": "Cache dependencies and build outputs",
|
||||
"type": "module",
|
||||
"main": "dist/restore/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc && ncc build -o dist/restore src/restore.ts && ncc build -o dist/save src/save.ts && ncc build -o dist/restore-only src/restoreOnly.ts && ncc build -o dist/save-only src/saveOnly.ts",
|
||||
"test": "tsc --noEmit && jest --coverage",
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
||||
"lint": "eslint **/*.ts --cache",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts"
|
||||
@@ -23,13 +24,13 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^5.0.5",
|
||||
"@actions/core": "^2.0.3",
|
||||
"@actions/exec": "^2.0.0",
|
||||
"@actions/io": "^2.0.0"
|
||||
"@actions/cache": "^6.0.1",
|
||||
"@actions/core": "^3.0.1",
|
||||
"@actions/exec": "^3.0.0",
|
||||
"@actions/io": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^24.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
@@ -41,11 +42,11 @@
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"jest": "^30.2.0",
|
||||
"nock": "^13.2.9",
|
||||
"prettier": "^3.6.2",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,63 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
"exclude": ["node_modules", "**/*.test.ts", "jest.config.ts", "__tests__"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user