mirror of
https://github.com/actions/setup-node
synced 2026-07-14 18:34:28 +02:00
Migrate to ESM and upgrade dependencies (#1574)
* Migrate to ESM * updated package.json version * Update README for v6 and clean up dev dependencies * Remove unused ts-node * fix audit issue --------- Co-authored-by: gowridurgad <gowridurgad@gmail.com>
This commit is contained in:
co-authored by
gowridurgad
parent
04be95cf35
commit
8207627860
+50
-21
@@ -1,19 +1,57 @@
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeAll,
|
||||
beforeEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as auth from '../src/authutil';
|
||||
import * as cacheUtils from '../src/cache-utils';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn((name: string) => {
|
||||
const val =
|
||||
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
||||
return val.trim();
|
||||
}),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn())
|
||||
}));
|
||||
|
||||
// Dynamic imports AFTER mocking so authutil sees the mocked core.
|
||||
const core = await import('@actions/core');
|
||||
const auth = await import('../src/authutil.js');
|
||||
|
||||
let rcFile: string;
|
||||
|
||||
describe('authutil tests', () => {
|
||||
const _runnerDir = path.join(__dirname, 'runner');
|
||||
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let logSpy: jest.SpyInstance;
|
||||
let dbgSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let logSpy: jest.SpiedFunction<typeof console.log>;
|
||||
|
||||
beforeAll(async () => {
|
||||
const randPath = path.join(Math.random().toString(36).substring(7));
|
||||
@@ -37,19 +75,8 @@ describe('authutil tests', () => {
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
logSpy = jest.spyOn(console, 'log');
|
||||
dbgSpy = jest.spyOn(core, 'debug');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
logSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + line + '\n');
|
||||
});
|
||||
dbgSpy.mockImplementation(msg => {
|
||||
// uncomment to see debug output
|
||||
// process.stderr.write(msg + '\n');
|
||||
});
|
||||
cnSpy.mockImplementation(() => true);
|
||||
logSpy.mockImplementation(() => {});
|
||||
}, 100000);
|
||||
|
||||
function dbg(message: string) {
|
||||
@@ -119,7 +146,8 @@ describe('authutil tests', () => {
|
||||
});
|
||||
|
||||
it('should not export NODE_AUTH_TOKEN if not set in environment', async () => {
|
||||
const exportSpy = jest.spyOn(core, 'exportVariable');
|
||||
const exportSpy = core.exportVariable as jest.Mock;
|
||||
exportSpy.mockClear();
|
||||
delete process.env.NODE_AUTH_TOKEN;
|
||||
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||
expect(fs.statSync(rcFile)).toBeDefined();
|
||||
@@ -132,7 +160,8 @@ describe('authutil tests', () => {
|
||||
});
|
||||
|
||||
it('should export NODE_AUTH_TOKEN if set to empty string', async () => {
|
||||
const exportSpy = jest.spyOn(core, 'exportVariable');
|
||||
const exportSpy = core.exportVariable as jest.Mock;
|
||||
exportSpy.mockClear();
|
||||
process.env.NODE_AUTH_TOKEN = '';
|
||||
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||
expect(fs.statSync(rcFile)).toBeDefined();
|
||||
|
||||
+123
-67
@@ -1,11 +1,67 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as cache from '@actions/cache';
|
||||
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import * as path from 'path';
|
||||
import * as glob from '@actions/glob';
|
||||
import osm from 'os';
|
||||
|
||||
import * as utils from '../src/cache-utils';
|
||||
import {restoreCache} from '../src/cache-restore';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/glob', () => ({
|
||||
hashFiles: jest.fn(),
|
||||
create: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const cache = await import('@actions/cache');
|
||||
const glob = await import('@actions/glob');
|
||||
const exec = await import('@actions/exec');
|
||||
const utils = await import('../src/cache-utils.js');
|
||||
const {restoreCache} = await import('../src/cache-restore.js');
|
||||
|
||||
describe('cache-restore', () => {
|
||||
const packageManagers = ['yarn', 'npm', 'pnpm'] as const;
|
||||
@@ -53,64 +109,66 @@ describe('cache-restore', () => {
|
||||
}
|
||||
}
|
||||
|
||||
let saveStateSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let setOutputSpy: jest.SpyInstance;
|
||||
let getCommandOutputSpy: jest.SpyInstance;
|
||||
let restoreCacheSpy: jest.SpyInstance;
|
||||
let hashFilesSpy: jest.SpyInstance;
|
||||
let archSpy: jest.SpyInstance;
|
||||
let saveStateSpy: jest.Mock;
|
||||
let infoSpy: jest.Mock;
|
||||
let debugSpy: jest.Mock;
|
||||
let setOutputSpy: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let restoreCacheSpy: jest.Mock;
|
||||
let hashFilesSpy: jest.Mock;
|
||||
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||
|
||||
beforeEach(() => {
|
||||
// core
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy = core.info as jest.Mock;
|
||||
infoSpy.mockImplementation(() => undefined);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy = core.debug as jest.Mock;
|
||||
debugSpy.mockImplementation(() => undefined);
|
||||
|
||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||
setOutputSpy = core.setOutput as jest.Mock;
|
||||
setOutputSpy.mockImplementation(() => undefined);
|
||||
|
||||
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||
saveStateSpy = core.saveState as jest.Mock;
|
||||
saveStateSpy.mockImplementation(() => undefined);
|
||||
|
||||
// glob
|
||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
||||
if (pattern.includes('package-lock.json')) {
|
||||
return npmFileHash;
|
||||
} else if (pattern.includes('pnpm-lock.yaml')) {
|
||||
return pnpmFileHash;
|
||||
} else if (pattern.includes('yarn.lock')) {
|
||||
return yarnFileHash;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// cache
|
||||
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
|
||||
restoreCacheSpy.mockImplementation(
|
||||
(cachePaths: Array<string>, key: string) => {
|
||||
if (!cachePaths || cachePaths.length === 0) {
|
||||
return undefined;
|
||||
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||
(hashFilesSpy as jest.Mock<typeof glob.hashFiles>).mockImplementation(
|
||||
async (pattern: string) => {
|
||||
if (pattern.includes('package-lock.json')) {
|
||||
return npmFileHash;
|
||||
} else if (pattern.includes('pnpm-lock.yaml')) {
|
||||
return pnpmFileHash;
|
||||
} else if (pattern.includes('yarn.lock')) {
|
||||
return yarnFileHash;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
const cachPath = cachePaths[0];
|
||||
const fileHash = cachesObject[cachPath];
|
||||
|
||||
if (key.includes(fileHash)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
|
||||
// cache-utils
|
||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||
// cache
|
||||
restoreCacheSpy = cache.restoreCache as jest.Mock;
|
||||
(
|
||||
restoreCacheSpy as jest.Mock<typeof cache.restoreCache>
|
||||
).mockImplementation(async (cachePaths: string[], key: string) => {
|
||||
if (!cachePaths || cachePaths.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cachPath = cachePaths[0];
|
||||
const fileHash = cachesObject[cachPath];
|
||||
|
||||
if (key.includes(fileHash)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// exec
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
|
||||
// os
|
||||
archSpy = jest.spyOn(osm, 'arch');
|
||||
@@ -134,19 +192,18 @@ describe('cache-restore', () => {
|
||||
['yarn', '1.2.3', yarnFileHash],
|
||||
['npm', '', npmFileHash],
|
||||
['pnpm', '', pnpmFileHash]
|
||||
] as const)(
|
||||
])(
|
||||
'restored dependencies for %s',
|
||||
async (packageManager, toolVersion, fileHash) => {
|
||||
const expectedCacheKey = `node-cache-${platform}-${arch}-${packageManager}-${fileHash}`;
|
||||
// Set workspace to the appropriate fixture folder
|
||||
setWorkspaceFor(packageManager);
|
||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
||||
if (command.includes('version')) {
|
||||
return toolVersion;
|
||||
} else {
|
||||
return findCacheFolder(command);
|
||||
}
|
||||
});
|
||||
setWorkspaceFor(packageManager as PackageManager);
|
||||
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||
stdout: command.includes('version')
|
||||
? toolVersion
|
||||
: findCacheFolder(command),
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
|
||||
await restoreCache(packageManager, '');
|
||||
expect(hashFilesSpy).toHaveBeenCalled();
|
||||
@@ -175,19 +232,18 @@ describe('cache-restore', () => {
|
||||
['yarn', '1.2.3', yarnFileHash],
|
||||
['npm', '', npmFileHash],
|
||||
['pnpm', '', pnpmFileHash]
|
||||
] as const)(
|
||||
])(
|
||||
'dependencies are changed %s',
|
||||
async (packageManager, toolVersion, fileHash) => {
|
||||
const expectedCacheKey = `node-cache-${platform}-${arch}-${packageManager}-${fileHash}`;
|
||||
// Set workspace to the appropriate fixture folder
|
||||
setWorkspaceFor(packageManager);
|
||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
||||
if (command.includes('version')) {
|
||||
return toolVersion;
|
||||
} else {
|
||||
return findCacheFolder(command);
|
||||
}
|
||||
});
|
||||
setWorkspaceFor(packageManager as PackageManager);
|
||||
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||
stdout: command.includes('version')
|
||||
? toolVersion
|
||||
: findCacheFolder(command),
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
|
||||
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
||||
await restoreCache(packageManager, '');
|
||||
|
||||
+116
-52
@@ -1,12 +1,74 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as cache from '@actions/cache';
|
||||
import * as glob from '@actions/glob';
|
||||
import fs from 'fs';
|
||||
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import * as utils from '../src/cache-utils';
|
||||
import {run} from '../src/cache-save';
|
||||
import {State} from '../src/constants';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn(),
|
||||
ValidationError: class ValidationError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/glob', () => ({
|
||||
hashFiles: jest.fn(),
|
||||
create: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const cache = await import('@actions/cache');
|
||||
const glob = await import('@actions/glob');
|
||||
const exec = await import('@actions/exec');
|
||||
const utils = await import('../src/cache-utils.js');
|
||||
const {run} = await import('../src/cache-save.js');
|
||||
const {State} = await import('../src/constants.js');
|
||||
|
||||
describe('run', () => {
|
||||
const yarnFileHash =
|
||||
@@ -20,42 +82,42 @@ describe('run', () => {
|
||||
|
||||
const inputs = {} as any;
|
||||
|
||||
let getInputSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let setFailedSpy: jest.SpyInstance;
|
||||
let getStateSpy: jest.SpyInstance;
|
||||
let saveCacheSpy: jest.SpyInstance;
|
||||
let getCommandOutputSpy: jest.SpyInstance;
|
||||
let hashFilesSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let getInputSpy: jest.Mock;
|
||||
let infoSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let debugSpy: jest.Mock;
|
||||
let setFailedSpy: jest.Mock;
|
||||
let getStateSpy: jest.Mock;
|
||||
let saveCacheSpy: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let hashFilesSpy: jest.Mock;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
|
||||
beforeEach(() => {
|
||||
getInputSpy = jest.spyOn(core, 'getInput');
|
||||
getInputSpy.mockImplementation((name: string) => inputs[name]);
|
||||
getInputSpy = core.getInput as jest.Mock;
|
||||
getInputSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy = core.info as jest.Mock;
|
||||
infoSpy.mockImplementation(() => undefined);
|
||||
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
warningSpy.mockImplementation(() => undefined);
|
||||
|
||||
setFailedSpy = jest.spyOn(core, 'setFailed');
|
||||
setFailedSpy = core.setFailed as jest.Mock;
|
||||
setFailedSpy.mockImplementation(() => undefined);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy = core.debug as jest.Mock;
|
||||
debugSpy.mockImplementation(() => undefined);
|
||||
|
||||
getStateSpy = jest.spyOn(core, 'getState');
|
||||
getStateSpy = core.getState as jest.Mock;
|
||||
|
||||
// cache
|
||||
saveCacheSpy = jest.spyOn(cache, 'saveCache');
|
||||
saveCacheSpy = cache.saveCache as jest.Mock;
|
||||
saveCacheSpy.mockImplementation(() => undefined);
|
||||
|
||||
// glob
|
||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
||||
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||
hashFilesSpy.mockImplementation((pattern: any) => {
|
||||
if (pattern.includes('package-lock.json')) {
|
||||
return npmFileHash;
|
||||
} else if (pattern.includes('yarn.lock')) {
|
||||
@@ -68,17 +130,20 @@ describe('run', () => {
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
existsSpy.mockImplementation(() => true);
|
||||
|
||||
// utils
|
||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||
// exec
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
existsSpy.mockRestore();
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Package manager validation', () => {
|
||||
it('Package manager is not provided, skip caching', async () => {
|
||||
inputs['cache'] = '';
|
||||
getStateSpy.mockImplementation(() => '');
|
||||
|
||||
await run();
|
||||
|
||||
@@ -124,7 +189,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||
@@ -148,7 +213,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||
@@ -167,13 +232,17 @@ describe('run', () => {
|
||||
? '["/foo/bar"]'
|
||||
: 'not expected'
|
||||
);
|
||||
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/npm`);
|
||||
getExecOutputSpy.mockImplementationOnce(() => ({
|
||||
stdout: `${commonPath}/npm`,
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
|
||||
await run();
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -194,7 +263,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -203,7 +272,7 @@ describe('run', () => {
|
||||
describe('action saves the cache', () => {
|
||||
it('saves cache from yarn 1', async () => {
|
||||
inputs['cache'] = 'yarn';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -219,7 +288,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||
@@ -233,7 +302,7 @@ describe('run', () => {
|
||||
|
||||
it('saves cache from yarn 2', async () => {
|
||||
inputs['cache'] = 'yarn';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -249,7 +318,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||
@@ -263,7 +332,7 @@ describe('run', () => {
|
||||
|
||||
it('saves cache from npm', async () => {
|
||||
inputs['cache'] = 'npm';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -279,7 +348,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||
@@ -293,7 +362,7 @@ describe('run', () => {
|
||||
|
||||
it('saves cache from pnpm', async () => {
|
||||
inputs['cache'] = 'pnpm';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -309,7 +378,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
||||
@@ -323,7 +392,7 @@ describe('run', () => {
|
||||
|
||||
it('save with -1 cacheId , should not fail workflow', async () => {
|
||||
inputs['cache'] = 'npm';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -342,8 +411,8 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenLastCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledWith(
|
||||
`Cache was not saved for the key: ${yarnFileHash}`
|
||||
);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
@@ -358,7 +427,7 @@ describe('run', () => {
|
||||
|
||||
it('saves with error from toolkit, should fail workflow', async () => {
|
||||
inputs['cache'] = 'npm';
|
||||
getStateSpy.mockImplementation((key: string) =>
|
||||
getStateSpy.mockImplementation((key: any) =>
|
||||
key === State.CachePackageManager
|
||||
? inputs['cache']
|
||||
: key === State.CacheMatchedKey
|
||||
@@ -377,7 +446,7 @@ describe('run', () => {
|
||||
|
||||
expect(getInputSpy).not.toHaveBeenCalled();
|
||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||
@@ -386,9 +455,4 @@ describe('run', () => {
|
||||
expect(setFailedSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
+176
-104
@@ -1,45 +1,133 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as cache from '@actions/cache';
|
||||
import path from 'path';
|
||||
import * as utils from '../src/cache-utils';
|
||||
import {
|
||||
PackageManagerInfo,
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/glob', () => ({
|
||||
hashFiles: jest.fn(),
|
||||
create: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const cache = await import('@actions/cache');
|
||||
const glob = await import('@actions/glob');
|
||||
const exec = await import('@actions/exec');
|
||||
const utils = await import('../src/cache-utils.js');
|
||||
const cacheUtils = await import('../src/cache-utils.js');
|
||||
// @ts-ignore - test file outside rootDir
|
||||
const {MockGlobber} = await import('./mock/glob-mock.js');
|
||||
|
||||
import type {PackageManagerInfo} from '../src/cache-utils.js';
|
||||
|
||||
const {
|
||||
isCacheFeatureAvailable,
|
||||
supportedPackageManagers,
|
||||
isGhes,
|
||||
resetProjectDirectoriesMemoized
|
||||
} from '../src/cache-utils';
|
||||
import fs from 'fs';
|
||||
import * as cacheUtils from '../src/cache-utils';
|
||||
import * as glob from '@actions/glob';
|
||||
import {Globber} from '@actions/glob';
|
||||
import {MockGlobber} from './mock/glob-mock';
|
||||
} = utils;
|
||||
|
||||
// Helper: mock exec.getExecOutput to simulate getCommandOutput behavior
|
||||
function mockGetCommandOutput(
|
||||
spy: jest.Mock,
|
||||
fn: (command: string, cwd?: string) => string
|
||||
) {
|
||||
spy.mockImplementation(async (command: any, _args: any, options: any) => ({
|
||||
stdout: fn(command, options?.cwd),
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
}
|
||||
|
||||
function mockGetCommandOutputOnce(spy: jest.Mock, result: string) {
|
||||
spy.mockImplementationOnce(async () => ({
|
||||
stdout: result,
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
}
|
||||
|
||||
describe('cache-utils', () => {
|
||||
const versionYarn1 = '1.2.3';
|
||||
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let getCommandOutputSpy: jest.SpyInstance;
|
||||
let isFeatureAvailable: jest.SpyInstance;
|
||||
let info: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let fsRealPathSyncSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let isFeatureAvailable: jest.Mock;
|
||||
let info: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let fsRealPathSyncSpy: jest.SpiedFunction<typeof fs.realpathSync>;
|
||||
|
||||
beforeEach(() => {
|
||||
console.log('::stop-commands::stoptoken');
|
||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy.mockImplementation(msg => {});
|
||||
|
||||
info = jest.spyOn(core, 'info');
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
debugSpy = core.debug as jest.Mock;
|
||||
debugSpy.mockImplementation((_msg: any) => {});
|
||||
|
||||
isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
info = core.info as jest.Mock;
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
|
||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||
isFeatureAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
|
||||
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
||||
fsRealPathSyncSpy.mockImplementation(dirName => {
|
||||
fsRealPathSyncSpy.mockImplementation((dirName: any) => {
|
||||
return dirName;
|
||||
});
|
||||
});
|
||||
@@ -47,7 +135,6 @@ describe('cache-utils', () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
//jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -64,7 +151,7 @@ describe('cache-utils', () => {
|
||||
['yarn2', null],
|
||||
['npm7', null]
|
||||
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
||||
getCommandOutputSpy.mockImplementationOnce(() => versionYarn1);
|
||||
mockGetCommandOutputOnce(getExecOutputSpy, versionYarn1);
|
||||
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
||||
result
|
||||
);
|
||||
@@ -92,7 +179,6 @@ describe('cache-utils', () => {
|
||||
|
||||
it('isCacheFeatureAvailable for GHES is available', () => {
|
||||
isFeatureAvailable.mockImplementation(() => true);
|
||||
|
||||
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
@@ -103,24 +189,22 @@ describe('cache-utils', () => {
|
||||
});
|
||||
|
||||
describe('getCacheDirectoriesPaths', () => {
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let lstatSpy: jest.SpyInstance;
|
||||
let globCreateSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
let lstatSpy: jest.SpiedFunction<typeof fs.lstatSync>;
|
||||
let globCreateSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
existsSpy.mockImplementation(() => true);
|
||||
|
||||
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
||||
lstatSpy.mockImplementation(arg => ({
|
||||
isDirectory: () => true
|
||||
}));
|
||||
lstatSpy.mockImplementation(
|
||||
(_arg: any) => ({isDirectory: () => true}) as any
|
||||
);
|
||||
|
||||
globCreateSpy = jest.spyOn(glob, 'create');
|
||||
|
||||
globCreateSpy.mockImplementation(
|
||||
(pattern: string): Promise<Globber> =>
|
||||
MockGlobber.create(['/foo', '/bar'])
|
||||
globCreateSpy = glob.create as jest.Mock;
|
||||
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||
MockGlobber.create(['/foo', '/bar'])
|
||||
);
|
||||
|
||||
resetProjectDirectoriesMemoized();
|
||||
@@ -129,7 +213,6 @@ describe('cache-utils', () => {
|
||||
afterEach(() => {
|
||||
existsSpy.mockRestore();
|
||||
lstatSpy.mockRestore();
|
||||
globCreateSpy.mockRestore();
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -142,22 +225,18 @@ describe('cache-utils', () => {
|
||||
])(
|
||||
'getCacheDirectoriesPaths should return one dir for non yarn',
|
||||
async (packageManagerInfo, cacheDependency) => {
|
||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
||||
|
||||
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
packageManagerInfo,
|
||||
cacheDependency
|
||||
);
|
||||
expect(dirs).toEqual(['foo']);
|
||||
// to do not call for a version
|
||||
// call once for get cache folder
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(1);
|
||||
}
|
||||
);
|
||||
|
||||
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
||||
|
||||
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
supportedPackageManagers.yarn,
|
||||
''
|
||||
@@ -178,15 +257,12 @@ describe('cache-utils', () => {
|
||||
])(
|
||||
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
||||
async (packageManagerInfo, cacheDependency) => {
|
||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||
// return empty string to indicate getCacheFolderPath failed
|
||||
// --version still works
|
||||
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||
command.includes('version') ? '1.' : ''
|
||||
);
|
||||
|
||||
await expect(
|
||||
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
||||
).rejects.toThrow(); //'Could not get cache folder path for /dir');
|
||||
).rejects.toThrow();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -196,10 +272,9 @@ describe('cache-utils', () => {
|
||||
])(
|
||||
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
||||
async (packageManagerInfo, cacheDependency) => {
|
||||
lstatSpy.mockImplementation(arg => ({
|
||||
isDirectory: () => false
|
||||
}));
|
||||
|
||||
lstatSpy.mockImplementation(
|
||||
(_arg: any) => ({isDirectory: () => false}) as any
|
||||
);
|
||||
await cacheUtils.getCacheDirectories(
|
||||
packageManagerInfo,
|
||||
cacheDependency
|
||||
@@ -214,9 +289,8 @@ describe('cache-utils', () => {
|
||||
it.each(['1.1.1', '2.2.2'])(
|
||||
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
||||
async version => {
|
||||
getCommandOutputSpy.mockImplementationOnce(() => version);
|
||||
getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`);
|
||||
|
||||
mockGetCommandOutputOnce(getExecOutputSpy, version);
|
||||
mockGetCommandOutputOnce(getExecOutputSpy, `foo${version}`);
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
supportedPackageManagers.yarn,
|
||||
''
|
||||
@@ -229,14 +303,12 @@ describe('cache-utils', () => {
|
||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
||||
async version => {
|
||||
let dirNo = 1;
|
||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||
);
|
||||
globCreateSpy.mockImplementation(
|
||||
(pattern: string): Promise<Globber> =>
|
||||
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
||||
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
||||
);
|
||||
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
supportedPackageManagers.yarn,
|
||||
'/tmp/**/file'
|
||||
@@ -249,18 +321,16 @@ describe('cache-utils', () => {
|
||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
||||
async version => {
|
||||
let dirNo = 1;
|
||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||
);
|
||||
globCreateSpy.mockImplementation(
|
||||
(pattern: string): Promise<Globber> =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir1/file'
|
||||
])
|
||||
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir1/file'
|
||||
])
|
||||
);
|
||||
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
supportedPackageManagers.yarn,
|
||||
'/tmp/**/file'
|
||||
@@ -273,55 +343,59 @@ describe('cache-utils', () => {
|
||||
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
||||
async version => {
|
||||
let dirNo = 1;
|
||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||
command.includes('version')
|
||||
? version
|
||||
: `file_${version}_${dirNo++ % 2}`
|
||||
);
|
||||
globCreateSpy.mockImplementation(
|
||||
(pattern: string): Promise<Globber> =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir3/file'
|
||||
])
|
||||
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir3/file'
|
||||
])
|
||||
);
|
||||
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
supportedPackageManagers.yarn,
|
||||
'/tmp/**/file'
|
||||
);
|
||||
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(6);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledTimes(6);
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
'yarn --version',
|
||||
'/tmp/dir1'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||
);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
'yarn --version',
|
||||
'/tmp/dir2'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||
);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
'yarn --version',
|
||||
'/tmp/dir3'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||
);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
version.startsWith('1.')
|
||||
? 'yarn cache dir'
|
||||
: 'yarn config get cacheFolder',
|
||||
'/tmp/dir1'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||
);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
version.startsWith('1.')
|
||||
? 'yarn cache dir'
|
||||
: 'yarn config get cacheFolder',
|
||||
'/tmp/dir2'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||
);
|
||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||
version.startsWith('1.')
|
||||
? 'yarn cache dir'
|
||||
: 'yarn config get cacheFolder',
|
||||
'/tmp/dir3'
|
||||
undefined,
|
||||
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -329,22 +403,20 @@ describe('cache-utils', () => {
|
||||
it.each(['1.1.1', '2.2.2'])(
|
||||
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
||||
async version => {
|
||||
// simulate wrong indents
|
||||
const cacheDependencyPath = `/tmp/dir1/file
|
||||
/tmp/dir2/file
|
||||
/tmp/**/file
|
||||
`;
|
||||
globCreateSpy.mockImplementation(
|
||||
(pattern: string): Promise<Globber> =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir3/file',
|
||||
'/tmp/dir4/file'
|
||||
])
|
||||
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||
MockGlobber.create([
|
||||
'/tmp/dir1/file',
|
||||
'/tmp/dir2/file',
|
||||
'/tmp/dir3/file',
|
||||
'/tmp/dir4/file'
|
||||
])
|
||||
);
|
||||
let dirNo = 1;
|
||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||
);
|
||||
const dirs = await cacheUtils.getCacheDirectories(
|
||||
|
||||
@@ -1,51 +1,162 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as cache from '@actions/cache';
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import cp from 'child_process';
|
||||
import osm from 'os';
|
||||
import path from 'path';
|
||||
import * as main from '../src/main';
|
||||
import * as auth from '../src/authutil';
|
||||
import {INodeVersion} from '../src/distributions/base-models';
|
||||
|
||||
import nodeTestManifest from './data/versions-manifest.json';
|
||||
import nodeTestDist from './data/node-dist-index.json';
|
||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||
import nodeTestDistRc from './data/node-rc-index.json';
|
||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||
find: jest.fn(),
|
||||
findAllVersions: jest.fn(),
|
||||
downloadTool: jest.fn(),
|
||||
extractTar: jest.fn(),
|
||||
extractZip: jest.fn(),
|
||||
extractXar: jest.fn(),
|
||||
extract7z: jest.fn(),
|
||||
cacheDir: jest.fn(),
|
||||
cacheFile: jest.fn(),
|
||||
getManifestFromRepo: jest.fn(),
|
||||
findFromManifest: jest.fn(),
|
||||
HTTPError: class HTTPError extends Error {
|
||||
readonly httpStatusCode: number;
|
||||
constructor(httpStatusCode?: number) {
|
||||
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||
this.httpStatusCode = httpStatusCode ?? 0;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const _mockGetJson = jest.fn();
|
||||
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||
HttpClient: jest.fn().mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}))
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
// Pre-import real authutil before mocking
|
||||
const realAuth = await import('../src/authutil.js');
|
||||
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||
...realAuth,
|
||||
configAuthentication: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const io = await import('@actions/io');
|
||||
const tc = await import('@actions/tool-cache');
|
||||
const httpm = await import('@actions/http-client');
|
||||
const exec = await import('@actions/exec');
|
||||
const cache = await import('@actions/cache');
|
||||
const main = await import('../src/main.js');
|
||||
const auth = await import('../src/authutil.js');
|
||||
|
||||
const {default: nodeTestManifest} = await import(
|
||||
'./data/versions-manifest.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeTestDistNightly} = await import(
|
||||
'./data/node-nightly-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeV8CanaryTestDist} = await import(
|
||||
'./data/v8-canary-dist-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
|
||||
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||
import type {IToolRelease} from '@actions/tool-cache';
|
||||
|
||||
describe('setup-node', () => {
|
||||
let inputs = {} as any;
|
||||
let os = {} as any;
|
||||
|
||||
let inSpy: jest.SpyInstance;
|
||||
let findSpy: jest.SpyInstance;
|
||||
let findAllVersionsSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let logSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let getManifestSpy: jest.SpyInstance;
|
||||
let getDistSpy: jest.SpyInstance;
|
||||
let platSpy: jest.SpyInstance;
|
||||
let archSpy: jest.SpyInstance;
|
||||
let dlSpy: jest.SpyInstance;
|
||||
let exSpy: jest.SpyInstance;
|
||||
let cacheSpy: jest.SpyInstance;
|
||||
let dbgSpy: jest.SpyInstance;
|
||||
let whichSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let readFileSyncSpy: jest.SpyInstance;
|
||||
let mkdirpSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let parseNodeVersionSpy: jest.SpyInstance;
|
||||
let isCacheActionAvailable: jest.SpyInstance;
|
||||
let getExecOutputSpy: jest.SpyInstance;
|
||||
let getJsonSpy: jest.SpyInstance;
|
||||
let inSpy: jest.Mock;
|
||||
let findSpy: jest.Mock;
|
||||
let findAllVersionsSpy: jest.Mock;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let logSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let addPathSpy: jest.Mock;
|
||||
let setFailedSpy: jest.Mock;
|
||||
let getManifestSpy: jest.Mock;
|
||||
let getDistSpy: jest.Mock;
|
||||
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||
let dlSpy: jest.Mock;
|
||||
let exSpy: jest.Mock;
|
||||
let cacheSpy: jest.Mock;
|
||||
let dbgSpy: jest.Mock;
|
||||
let whichSpy: jest.Mock;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
let readFileSyncSpy: jest.Mock;
|
||||
let mkdirpSpy: jest.Mock;
|
||||
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||
let authSpy: jest.Mock;
|
||||
let parseNodeVersionSpy: jest.Mock;
|
||||
let isCacheActionAvailable: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let getJsonSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
@@ -53,8 +164,8 @@ describe('setup-node', () => {
|
||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
inputs = {};
|
||||
inSpy = jest.spyOn(core, 'getInput');
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy = core.getInput as jest.Mock;
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
// node
|
||||
os = {};
|
||||
@@ -65,34 +176,35 @@ describe('setup-node', () => {
|
||||
execSpy = jest.spyOn(cp, 'execSync');
|
||||
|
||||
// @actions/tool-cache
|
||||
findSpy = jest.spyOn(tc, 'find');
|
||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||
exSpy = jest.spyOn(tc, 'extractTar');
|
||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||
findSpy = tc.find as jest.Mock;
|
||||
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||
dlSpy = tc.downloadTool as jest.Mock;
|
||||
exSpy = tc.extractTar as jest.Mock;
|
||||
cacheSpy = tc.cacheDir as jest.Mock;
|
||||
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||
|
||||
// http-client
|
||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||
getJsonSpy = _mockGetJson;
|
||||
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}));
|
||||
|
||||
// io
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
whichSpy = io.which as jest.Mock;
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||
|
||||
// @actions/tool-cache
|
||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
|
||||
// disable authentication portion for installer tests
|
||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||
authSpy = auth.configAuthentication as jest.Mock;
|
||||
authSpy.mockImplementation(() => {});
|
||||
|
||||
// gets
|
||||
getManifestSpy.mockImplementation(
|
||||
() => <tc.IToolRelease[]>nodeTestManifest
|
||||
);
|
||||
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||
|
||||
getJsonSpy.mockImplementation(url => {
|
||||
getJsonSpy.mockImplementation((url: any) => {
|
||||
let res: any;
|
||||
if (url.includes('/rc')) {
|
||||
res = <INodeVersion[]>nodeTestDistRc;
|
||||
@@ -109,28 +221,18 @@ describe('setup-node', () => {
|
||||
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
logSpy = jest.spyOn(core, 'info');
|
||||
dbgSpy = jest.spyOn(core, 'debug');
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
logSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + line + '\n');
|
||||
});
|
||||
dbgSpy.mockImplementation(msg => {
|
||||
// uncomment to see debug output
|
||||
// process.stderr.write(msg + '\n');
|
||||
});
|
||||
warningSpy.mockImplementation(msg => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + msg + '\n');
|
||||
});
|
||||
logSpy = core.info as jest.Mock;
|
||||
dbgSpy = core.debug as jest.Mock;
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
addPathSpy = core.addPath as jest.Mock;
|
||||
setFailedSpy = core.setFailed as jest.Mock;
|
||||
cnSpy.mockImplementation(() => true);
|
||||
logSpy.mockImplementation(() => {});
|
||||
dbgSpy.mockImplementation(() => {});
|
||||
warningSpy.mockImplementation(() => {});
|
||||
|
||||
// @actions/exec
|
||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||
});
|
||||
|
||||
@@ -178,7 +280,7 @@ describe('setup-node', () => {
|
||||
inputs['node-version'] = '20-v8-canary';
|
||||
os['arch'] = 'x64';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize(
|
||||
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
||||
@@ -193,7 +295,7 @@ describe('setup-node', () => {
|
||||
await main.run();
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('handles unhandled find error and reports error', async () => {
|
||||
@@ -213,7 +315,7 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
//--------------------------------------------------
|
||||
@@ -249,7 +351,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('does not find a version that does not exist', async () => {
|
||||
@@ -268,8 +370,8 @@ describe('setup-node', () => {
|
||||
]);
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -295,7 +397,7 @@ describe('setup-node', () => {
|
||||
});
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('acquires specified architecture of node', async () => {
|
||||
@@ -319,7 +421,6 @@ describe('setup-node', () => {
|
||||
darwin: 'darwin',
|
||||
win32: 'win'
|
||||
}[os.platform];
|
||||
|
||||
inputs['node-version'] = version;
|
||||
inputs['architecture'] = arch;
|
||||
inputs['token'] = 'faketoken';
|
||||
@@ -393,9 +494,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -428,9 +527,7 @@ describe('setup-node', () => {
|
||||
// assert
|
||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -490,9 +587,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -554,9 +649,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -575,13 +668,11 @@ describe('setup-node', () => {
|
||||
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
||||
|
||||
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
||||
findSpy.mockImplementation(version => toolPath);
|
||||
findSpy.mockImplementation((version: any) => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${toolPath}${path.sep}bin${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(`${toolPath}${path.sep}bin`);
|
||||
|
||||
expect(dlSpy).not.toHaveBeenCalled();
|
||||
expect(exSpy).not.toHaveBeenCalled();
|
||||
|
||||
+155
-63
@@ -1,43 +1,125 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as cache from '@actions/cache';
|
||||
import * as io from '@actions/io';
|
||||
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import osm from 'os';
|
||||
|
||||
import each from 'jest-each';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
import * as main from '../src/main';
|
||||
import * as util from '../src/util';
|
||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||
find: jest.fn(),
|
||||
findAllVersions: jest.fn(),
|
||||
downloadTool: jest.fn(),
|
||||
extractTar: jest.fn(),
|
||||
extractZip: jest.fn(),
|
||||
extractXar: jest.fn(),
|
||||
extract7z: jest.fn(),
|
||||
cacheDir: jest.fn(),
|
||||
cacheFile: jest.fn(),
|
||||
getManifestFromRepo: jest.fn(),
|
||||
findFromManifest: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
// Pre-import real util before mocking so we can spread it
|
||||
const realUtil = await import('../src/util.js');
|
||||
|
||||
jest.unstable_mockModule('../src/util.js', () => ({
|
||||
...realUtil,
|
||||
getNodeVersionFromFile: jest.fn(realUtil.getNodeVersionFromFile)
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const exec = await import('@actions/exec');
|
||||
const tc = await import('@actions/tool-cache');
|
||||
const cache = await import('@actions/cache');
|
||||
const io = await import('@actions/io');
|
||||
const main = await import('../src/main.js');
|
||||
const util = await import('../src/util.js');
|
||||
const {default: OfficialBuilds} =
|
||||
await import('../src/distributions/official_builds/official_builds.js');
|
||||
|
||||
import each from 'jest-each';
|
||||
|
||||
describe('main tests', () => {
|
||||
let inputs = {} as any;
|
||||
let os = {} as any;
|
||||
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let saveStateSpy: jest.SpyInstance;
|
||||
let inSpy: jest.SpyInstance;
|
||||
let setOutputSpy: jest.SpyInstance;
|
||||
let startGroupSpy: jest.SpyInstance;
|
||||
let endGroupSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let saveStateSpy: jest.Mock;
|
||||
let inSpy: jest.Mock;
|
||||
let setOutputSpy: jest.Mock;
|
||||
let startGroupSpy: jest.Mock;
|
||||
let endGroupSpy: jest.Mock;
|
||||
|
||||
let whichSpy: jest.SpyInstance;
|
||||
let whichSpy: jest.Mock;
|
||||
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.Mock;
|
||||
|
||||
let getExecOutputSpy: jest.SpyInstance;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
|
||||
let getNodeVersionFromFileSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let findSpy: jest.SpyInstance;
|
||||
let isCacheActionAvailable: jest.SpyInstance;
|
||||
let getNodeVersionFromFileSpy: jest.Mock;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let findSpy: jest.Mock;
|
||||
let isCacheActionAvailable: jest.Mock;
|
||||
|
||||
let setupNodeJsSpy: jest.SpyInstance;
|
||||
let setupNodeJsSpy: jest.SpiedFunction<
|
||||
typeof OfficialBuilds.prototype.setupNodeJs
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
inputs = {};
|
||||
@@ -48,37 +130,34 @@ describe('main tests', () => {
|
||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy = core.info as jest.Mock;
|
||||
infoSpy.mockImplementation(() => {});
|
||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||
setOutputSpy = core.setOutput as jest.Mock;
|
||||
setOutputSpy.mockImplementation(() => {});
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
warningSpy.mockImplementation(() => {});
|
||||
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||
saveStateSpy = core.saveState as jest.Mock;
|
||||
saveStateSpy.mockImplementation(() => {});
|
||||
startGroupSpy = jest.spyOn(core, 'startGroup');
|
||||
startGroupSpy = core.startGroup as jest.Mock;
|
||||
startGroupSpy.mockImplementation(() => {});
|
||||
endGroupSpy = jest.spyOn(core, 'endGroup');
|
||||
endGroupSpy = core.endGroup as jest.Mock;
|
||||
endGroupSpy.mockImplementation(() => {});
|
||||
inSpy = jest.spyOn(core, 'getInput');
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy = core.getInput as jest.Mock;
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
whichSpy = io.which as jest.Mock;
|
||||
|
||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
|
||||
findSpy = jest.spyOn(tc, 'find');
|
||||
findSpy = tc.find as jest.Mock;
|
||||
|
||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
cnSpy.mockImplementation(() => true);
|
||||
|
||||
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
||||
setupNodeJsSpy.mockImplementation(() => {});
|
||||
setupNodeJsSpy.mockImplementation(async () => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -93,6 +172,12 @@ describe('main tests', () => {
|
||||
}, 100000);
|
||||
|
||||
describe('getNodeVersionFromFile', () => {
|
||||
beforeEach(() => {
|
||||
(util.getNodeVersionFromFile as jest.Mock).mockImplementation(
|
||||
realUtil.getNodeVersionFromFile as any
|
||||
);
|
||||
});
|
||||
|
||||
each`
|
||||
contents | expected
|
||||
${'12'} | ${'12'}
|
||||
@@ -112,12 +197,12 @@ describe('main tests', () => {
|
||||
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
||||
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
||||
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
||||
`.it('parses "$contents"', ({contents, expected}) => {
|
||||
`.it('parses "$contents"', ({contents, expected}: any) => {
|
||||
const existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
existsSpy.mockImplementation(() => true);
|
||||
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(filePath => {
|
||||
readFileSpy.mockImplementation((filePath: any) => {
|
||||
if (
|
||||
typeof filePath === 'string' &&
|
||||
path.basename(filePath) === 'package.json'
|
||||
@@ -139,8 +224,10 @@ describe('main tests', () => {
|
||||
[{node: '16.0.2', npm: '7.3.3', yarn: '2.22.11'}],
|
||||
[{node: '14.0.1', npm: '8.1.0', yarn: '3.2.1'}],
|
||||
[{node: '17.0.2', npm: '6.3.3', yarn: ''}]
|
||||
])('Tools versions %p', async obj => {
|
||||
getExecOutputSpy.mockImplementation(async command => {
|
||||
])('Tools versions %p', async (obj: any) => {
|
||||
(
|
||||
getExecOutputSpy as jest.Mock<typeof exec.getExecOutput>
|
||||
).mockImplementation(async (command: string) => {
|
||||
if (Reflect.has(obj, command) && !obj[command]) {
|
||||
return {
|
||||
stdout: '',
|
||||
@@ -152,14 +239,14 @@ describe('main tests', () => {
|
||||
return {stdout: obj[command], stderr: '', exitCode: 0};
|
||||
});
|
||||
|
||||
whichSpy.mockImplementation(cmd => {
|
||||
whichSpy.mockImplementation((cmd: any) => {
|
||||
return `some/${cmd}/path`;
|
||||
});
|
||||
|
||||
await util.printEnvDetailsAndSetOutput();
|
||||
|
||||
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
||||
Object.getOwnPropertyNames(obj).forEach(name => {
|
||||
Object.getOwnPropertyNames(obj).forEach((name: any) => {
|
||||
if (!obj[name]) {
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
`[warning]${name} does not exist`
|
||||
@@ -175,11 +262,16 @@ describe('main tests', () => {
|
||||
delete inputs['node-version'];
|
||||
inputs['node-version-file'] = '.nvmrc';
|
||||
|
||||
getNodeVersionFromFileSpy = jest.spyOn(util, 'getNodeVersionFromFile');
|
||||
getNodeVersionFromFileSpy = util.getNodeVersionFromFile as jest.Mock;
|
||||
getNodeVersionFromFileSpy.mockImplementation(
|
||||
realUtil.getNodeVersionFromFile as any
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
getNodeVersionFromFileSpy.mockRestore();
|
||||
getNodeVersionFromFileSpy.mockImplementation(
|
||||
realUtil.getNodeVersionFromFile as any
|
||||
);
|
||||
});
|
||||
|
||||
it('does not read node-version-file if node-version is provided', async () => {
|
||||
@@ -238,8 +330,8 @@ describe('main tests', () => {
|
||||
|
||||
// Assert
|
||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
|
||||
expect(core.setFailed as jest.Mock).toHaveBeenCalledWith(
|
||||
`The specified node version file at: ${versionFilePath} does not exist`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -249,7 +341,7 @@ describe('main tests', () => {
|
||||
inputs['node-version'] = '12';
|
||||
inputs['cache'] = 'npm';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
@@ -269,7 +361,7 @@ describe('main tests', () => {
|
||||
inputs['node-version'] = '12';
|
||||
inputs['cache'] = 'npm';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
@@ -292,7 +384,7 @@ describe('main tests', () => {
|
||||
inputs['cache'] = '';
|
||||
isCacheActionAvailable.mockImplementation(() => true);
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -310,7 +402,7 @@ describe('main tests', () => {
|
||||
inputs['cache'] = '';
|
||||
isCacheActionAvailable.mockImplementation(() => true);
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -330,7 +422,7 @@ describe('main tests', () => {
|
||||
inputs['cache'] = '';
|
||||
isCacheActionAvailable.mockImplementation(() => true);
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -348,7 +440,7 @@ describe('main tests', () => {
|
||||
it('Should not enable caching if packageManager is "pnpm@8.0.0" and cache input is not provided', async () => {
|
||||
inputs['package-manager-cache'] = 'true';
|
||||
inputs['cache'] = '';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -364,7 +456,7 @@ describe('main tests', () => {
|
||||
it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
|
||||
inputs['package-manager-cache'] = 'true';
|
||||
inputs['cache'] = '';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -382,7 +474,7 @@ describe('main tests', () => {
|
||||
it('Should not enable caching if devEngines.packageManager is array without "npm"', async () => {
|
||||
inputs['package-manager-cache'] = 'true';
|
||||
inputs['cache'] = '';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -400,7 +492,7 @@ describe('main tests', () => {
|
||||
it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
|
||||
inputs['package-manager-cache'] = 'true';
|
||||
inputs['cache'] = '';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||
readFileSpy.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
@@ -416,7 +508,7 @@ describe('main tests', () => {
|
||||
it('Should skip caching when package-manager-cache is false', async () => {
|
||||
inputs['package-manager-cache'] = 'false';
|
||||
inputs['cache'] = '';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
await main.run();
|
||||
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -424,7 +516,7 @@ describe('main tests', () => {
|
||||
it('Should enable caching with cache input explicitly provided', async () => {
|
||||
inputs['package-manager-cache'] = 'true';
|
||||
inputs['cache'] = 'npm';
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
isCacheActionAvailable.mockImplementation(() => true);
|
||||
await main.run();
|
||||
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {MockGlobber} from './glob-mock';
|
||||
import {describe, it, expect} from '@jest/globals';
|
||||
import {MockGlobber} from './glob-mock.js';
|
||||
|
||||
describe('mocked globber tests', () => {
|
||||
it('globber should return generator', async () => {
|
||||
|
||||
+193
-101
@@ -1,51 +1,160 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as cache from '@actions/cache';
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import cp from 'child_process';
|
||||
import osm from 'os';
|
||||
import path from 'path';
|
||||
import * as main from '../src/main';
|
||||
import * as auth from '../src/authutil';
|
||||
import {INodeVersion} from '../src/distributions/base-models';
|
||||
|
||||
import nodeTestManifest from './data/versions-manifest.json';
|
||||
import nodeTestDist from './data/node-dist-index.json';
|
||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||
import nodeTestDistRc from './data/node-rc-index.json';
|
||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||
find: jest.fn(),
|
||||
findAllVersions: jest.fn(),
|
||||
downloadTool: jest.fn(),
|
||||
extractTar: jest.fn(),
|
||||
extractZip: jest.fn(),
|
||||
extractXar: jest.fn(),
|
||||
extract7z: jest.fn(),
|
||||
cacheDir: jest.fn(),
|
||||
cacheFile: jest.fn(),
|
||||
getManifestFromRepo: jest.fn(),
|
||||
findFromManifest: jest.fn(),
|
||||
HTTPError: class HTTPError extends Error {
|
||||
readonly httpStatusCode: number;
|
||||
constructor(httpStatusCode?: number) {
|
||||
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||
this.httpStatusCode = httpStatusCode ?? 0;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const _mockGetJson = jest.fn();
|
||||
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||
HttpClient: jest.fn().mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}))
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
const realAuth = await import('../src/authutil.js');
|
||||
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||
...realAuth,
|
||||
configAuthentication: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const io = await import('@actions/io');
|
||||
const tc = await import('@actions/tool-cache');
|
||||
const httpm = await import('@actions/http-client');
|
||||
const exec = await import('@actions/exec');
|
||||
const cache = await import('@actions/cache');
|
||||
const main = await import('../src/main.js');
|
||||
const auth = await import('../src/authutil.js');
|
||||
|
||||
const {default: nodeTestManifest} = await import(
|
||||
'./data/versions-manifest.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeTestDistNightly} = await import(
|
||||
'./data/node-nightly-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeV8CanaryTestDist} = await import(
|
||||
'./data/v8-canary-dist-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
|
||||
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||
|
||||
describe('setup-node', () => {
|
||||
let inputs = {} as any;
|
||||
let os = {} as any;
|
||||
|
||||
let inSpy: jest.SpyInstance;
|
||||
let findSpy: jest.SpyInstance;
|
||||
let findAllVersionsSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let logSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let getManifestSpy: jest.SpyInstance;
|
||||
let getDistSpy: jest.SpyInstance;
|
||||
let platSpy: jest.SpyInstance;
|
||||
let archSpy: jest.SpyInstance;
|
||||
let dlSpy: jest.SpyInstance;
|
||||
let exSpy: jest.SpyInstance;
|
||||
let cacheSpy: jest.SpyInstance;
|
||||
let dbgSpy: jest.SpyInstance;
|
||||
let whichSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let mkdirpSpy: jest.SpyInstance;
|
||||
let cpSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let parseNodeVersionSpy: jest.SpyInstance;
|
||||
let isCacheActionAvailable: jest.SpyInstance;
|
||||
let getExecOutputSpy: jest.SpyInstance;
|
||||
let getJsonSpy: jest.SpyInstance;
|
||||
let inSpy: jest.Mock;
|
||||
let findSpy: jest.Mock;
|
||||
let findAllVersionsSpy: jest.Mock;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let logSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let addPathSpy: jest.Mock;
|
||||
let setFailedSpy: jest.Mock;
|
||||
let getManifestSpy: jest.Mock;
|
||||
let getDistSpy: jest.Mock;
|
||||
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||
let dlSpy: jest.Mock;
|
||||
let exSpy: jest.Mock;
|
||||
let cacheSpy: jest.Mock;
|
||||
let dbgSpy: jest.Mock;
|
||||
let whichSpy: jest.Mock;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
let mkdirpSpy: jest.Mock;
|
||||
let cpSpy: jest.Mock;
|
||||
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||
let authSpy: jest.Mock;
|
||||
let parseNodeVersionSpy: jest.Mock;
|
||||
let isCacheActionAvailable: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let getJsonSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
@@ -54,8 +163,8 @@ describe('setup-node', () => {
|
||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
process.env['RUNNER_TEMP'] = '/runner_temp';
|
||||
inputs = {};
|
||||
inSpy = jest.spyOn(core, 'getInput');
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy = core.getInput as jest.Mock;
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
// node
|
||||
os = {};
|
||||
@@ -66,30 +175,33 @@ describe('setup-node', () => {
|
||||
execSpy = jest.spyOn(cp, 'execSync');
|
||||
|
||||
// @actions/tool-cache
|
||||
findSpy = jest.spyOn(tc, 'find');
|
||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||
exSpy = jest.spyOn(tc, 'extractTar');
|
||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||
findSpy = tc.find as jest.Mock;
|
||||
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||
dlSpy = tc.downloadTool as jest.Mock;
|
||||
exSpy = tc.extractTar as jest.Mock;
|
||||
cacheSpy = tc.cacheDir as jest.Mock;
|
||||
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||
|
||||
// http-client
|
||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||
getJsonSpy = _mockGetJson;
|
||||
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}));
|
||||
|
||||
// io
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
whichSpy = io.which as jest.Mock;
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||
cpSpy = jest.spyOn(io, 'cp');
|
||||
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||
cpSpy = io.cp as jest.Mock;
|
||||
|
||||
// @actions/tool-cache
|
||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
|
||||
// disable authentication portion for installer tests
|
||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||
authSpy = auth.configAuthentication as jest.Mock;
|
||||
authSpy.mockImplementation(() => {});
|
||||
|
||||
getJsonSpy.mockImplementation(url => {
|
||||
getJsonSpy.mockImplementation((url: any) => {
|
||||
let res: any;
|
||||
if (url.includes('/rc')) {
|
||||
res = <INodeVersion[]>nodeTestDistRc;
|
||||
@@ -106,28 +218,18 @@ describe('setup-node', () => {
|
||||
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
logSpy = jest.spyOn(core, 'info');
|
||||
dbgSpy = jest.spyOn(core, 'debug');
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
logSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + line + '\n');
|
||||
});
|
||||
dbgSpy.mockImplementation(msg => {
|
||||
// uncomment to see debug output
|
||||
// process.stderr.write(msg + '\n');
|
||||
});
|
||||
warningSpy.mockImplementation(msg => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + msg + '\n');
|
||||
});
|
||||
logSpy = core.info as jest.Mock;
|
||||
dbgSpy = core.debug as jest.Mock;
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
addPathSpy = core.addPath as jest.Mock;
|
||||
setFailedSpy = core.setFailed as jest.Mock;
|
||||
cnSpy.mockImplementation(() => true);
|
||||
logSpy.mockImplementation(() => {});
|
||||
dbgSpy.mockImplementation(() => {});
|
||||
warningSpy.mockImplementation(() => {});
|
||||
|
||||
// @actions/exec
|
||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||
});
|
||||
|
||||
@@ -202,7 +304,7 @@ describe('setup-node', () => {
|
||||
inputs['node-version'] = '16-nightly';
|
||||
os['arch'] = 'x64';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize(
|
||||
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
||||
@@ -224,7 +326,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('handles unhandled find error and reports error', async () => {
|
||||
@@ -244,7 +346,7 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('falls back to a version from node dist', async () => {
|
||||
@@ -274,7 +376,7 @@ describe('setup-node', () => {
|
||||
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(exSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
||||
@@ -296,8 +398,8 @@ describe('setup-node', () => {
|
||||
findSpy.mockImplementation(() => '');
|
||||
findAllVersionsSpy.mockImplementation(() => []);
|
||||
|
||||
dlSpy.mockImplementation(async url => {
|
||||
if (workingUrls.includes(url)) {
|
||||
dlSpy.mockImplementation(async (url: any) => {
|
||||
if (workingUrls.includes(url as string)) {
|
||||
return '/some/temp/path';
|
||||
}
|
||||
|
||||
@@ -312,10 +414,10 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
workingUrls.forEach(url => {
|
||||
workingUrls.forEach((url: any) => {
|
||||
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
|
||||
});
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${toolPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(toolPath);
|
||||
});
|
||||
|
||||
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
||||
@@ -337,8 +439,8 @@ describe('setup-node', () => {
|
||||
findSpy.mockImplementation(() => '');
|
||||
findAllVersionsSpy.mockImplementation(() => []);
|
||||
|
||||
dlSpy.mockImplementation(async url => {
|
||||
if (workingUrls.includes(url)) {
|
||||
dlSpy.mockImplementation(async (url: any) => {
|
||||
if (workingUrls.includes(url as string)) {
|
||||
return '/some/temp/path';
|
||||
}
|
||||
|
||||
@@ -353,12 +455,10 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
workingUrls.forEach(url => {
|
||||
workingUrls.forEach((url: any) => {
|
||||
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
||||
});
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unexpected HTTP response: 404${osm.EOL}`
|
||||
);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith('Unexpected HTTP response: 404');
|
||||
});
|
||||
|
||||
it('does not find a version that does not exist', async () => {
|
||||
@@ -372,8 +472,8 @@ describe('setup-node', () => {
|
||||
findAllVersionsSpy.mockImplementation(() => []);
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -396,7 +496,7 @@ describe('setup-node', () => {
|
||||
});
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('acquires specified architecture of node', async () => {
|
||||
@@ -420,7 +520,6 @@ describe('setup-node', () => {
|
||||
darwin: 'darwin',
|
||||
win32: 'win'
|
||||
}[os.platform];
|
||||
|
||||
inputs['node-version'] = version;
|
||||
inputs['architecture'] = arch;
|
||||
inputs['token'] = 'faketoken';
|
||||
@@ -465,7 +564,6 @@ describe('setup-node', () => {
|
||||
darwin: 'darwin',
|
||||
win32: 'win'
|
||||
}[os.platform];
|
||||
|
||||
inputs['node-version'] = version;
|
||||
inputs['architecture'] = arch;
|
||||
inputs['token'] = 'faketoken';
|
||||
@@ -541,9 +639,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -573,9 +669,7 @@ describe('setup-node', () => {
|
||||
// assert
|
||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -641,9 +735,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,51 +1,163 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as cache from '@actions/cache';
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import cp from 'child_process';
|
||||
import osm from 'os';
|
||||
import path from 'path';
|
||||
import * as main from '../src/main';
|
||||
import * as auth from '../src/authutil';
|
||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
||||
import {INodeVersion} from '../src/distributions/base-models';
|
||||
|
||||
import nodeTestManifest from './data/versions-manifest.json';
|
||||
import nodeTestDist from './data/node-dist-index.json';
|
||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||
import nodeTestDistRc from './data/node-rc-index.json';
|
||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||
find: jest.fn(),
|
||||
findAllVersions: jest.fn(),
|
||||
downloadTool: jest.fn(),
|
||||
extractTar: jest.fn(),
|
||||
extractZip: jest.fn(),
|
||||
extractXar: jest.fn(),
|
||||
extract7z: jest.fn(),
|
||||
cacheDir: jest.fn(),
|
||||
cacheFile: jest.fn(),
|
||||
getManifestFromRepo: jest.fn(),
|
||||
findFromManifest: jest.fn(),
|
||||
HTTPError: class HTTPError extends Error {
|
||||
readonly httpStatusCode: number;
|
||||
constructor(httpStatusCode?: number) {
|
||||
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||
this.httpStatusCode = httpStatusCode ?? 0;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const _mockGetJson = jest.fn();
|
||||
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||
HttpClient: jest.fn().mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}))
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
// Pre-import real authutil before mocking
|
||||
const realAuth = await import('../src/authutil.js');
|
||||
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||
...realAuth,
|
||||
configAuthentication: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const io = await import('@actions/io');
|
||||
const tc = await import('@actions/tool-cache');
|
||||
const httpm = await import('@actions/http-client');
|
||||
const exec = await import('@actions/exec');
|
||||
const cache = await import('@actions/cache');
|
||||
const main = await import('../src/main.js');
|
||||
const auth = await import('../src/authutil.js');
|
||||
const {default: OfficialBuilds} =
|
||||
await import('../src/distributions/official_builds/official_builds.js');
|
||||
|
||||
const {default: nodeTestManifest} = await import(
|
||||
'./data/versions-manifest.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeTestDistNightly} = await import(
|
||||
'./data/node-nightly-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeV8CanaryTestDist} = await import(
|
||||
'./data/v8-canary-dist-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
|
||||
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||
import type {IToolRelease} from '@actions/tool-cache';
|
||||
|
||||
describe('setup-node', () => {
|
||||
let build: OfficialBuilds;
|
||||
let build: InstanceType<typeof OfficialBuilds>;
|
||||
let inputs = {} as any;
|
||||
let os = {} as any;
|
||||
|
||||
let inSpy: jest.SpyInstance;
|
||||
let findSpy: jest.SpyInstance;
|
||||
let findAllVersionsSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let logSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let getManifestSpy: jest.SpyInstance;
|
||||
let platSpy: jest.SpyInstance;
|
||||
let archSpy: jest.SpyInstance;
|
||||
let dlSpy: jest.SpyInstance;
|
||||
let exSpy: jest.SpyInstance;
|
||||
let cacheSpy: jest.SpyInstance;
|
||||
let dbgSpy: jest.SpyInstance;
|
||||
let whichSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let readFileSyncSpy: jest.SpyInstance;
|
||||
let mkdirpSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let isCacheActionAvailable: jest.SpyInstance;
|
||||
let getExecOutputSpy: jest.SpyInstance;
|
||||
let getJsonSpy: jest.SpyInstance;
|
||||
let inSpy: jest.Mock;
|
||||
let findSpy: jest.Mock;
|
||||
let findAllVersionsSpy: jest.Mock;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let logSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let addPathSpy: jest.Mock;
|
||||
let setFailedSpy: jest.Mock;
|
||||
let getManifestSpy: jest.Mock;
|
||||
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||
let dlSpy: jest.Mock;
|
||||
let exSpy: jest.Mock;
|
||||
let cacheSpy: jest.Mock;
|
||||
let dbgSpy: jest.Mock;
|
||||
let whichSpy: jest.Mock;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
let readFileSyncSpy: jest.Mock;
|
||||
let mkdirpSpy: jest.Mock;
|
||||
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||
let authSpy: jest.Mock;
|
||||
let isCacheActionAvailable: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let getJsonSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
@@ -53,8 +165,8 @@ describe('setup-node', () => {
|
||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
inputs = {};
|
||||
inSpy = jest.spyOn(core, 'getInput');
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy = core.getInput as jest.Mock;
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
// node
|
||||
os = {};
|
||||
@@ -65,34 +177,61 @@ describe('setup-node', () => {
|
||||
execSpy = jest.spyOn(cp, 'execSync');
|
||||
|
||||
// @actions/tool-cache
|
||||
findSpy = jest.spyOn(tc, 'find');
|
||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||
exSpy = jest.spyOn(tc, 'extractTar');
|
||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||
findSpy = tc.find as jest.Mock;
|
||||
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||
dlSpy = tc.downloadTool as jest.Mock;
|
||||
exSpy = tc.extractTar as jest.Mock;
|
||||
cacheSpy = tc.cacheDir as jest.Mock;
|
||||
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||
|
||||
// Restore findFromManifest after clearMocks.
|
||||
// In ESM, the real tc.findFromManifest uses os.platform() from its own
|
||||
// module namespace, which jest.spyOn on our os import cannot override.
|
||||
// So we provide a minimal reimplementation that uses the test's os var.
|
||||
(tc.findFromManifest as jest.Mock).mockImplementation(
|
||||
async (versionSpec: any, stable: any, manifest: any, archFilter: any) => {
|
||||
const arch = archFilter || os['arch'] || 'x64';
|
||||
const plat = os['platform'] || process.platform;
|
||||
const semverModule = await import('semver');
|
||||
for (const rel of manifest || []) {
|
||||
if (
|
||||
semverModule.default.satisfies(rel.version, versionSpec, {
|
||||
includePrerelease: true
|
||||
}) &&
|
||||
rel.stable === stable
|
||||
) {
|
||||
const file = rel.files.find(
|
||||
(f: any) => f.arch === arch && f.platform === plat
|
||||
);
|
||||
if (file) return {...rel, files: [file]};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
|
||||
// http-client
|
||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||
getJsonSpy = _mockGetJson;
|
||||
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}));
|
||||
|
||||
// io
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
whichSpy = io.which as jest.Mock;
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||
|
||||
// @actions/tool-cache
|
||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
|
||||
// disable authentication portion for installer tests
|
||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||
authSpy = auth.configAuthentication as jest.Mock;
|
||||
authSpy.mockImplementation(() => {});
|
||||
|
||||
// gets
|
||||
getManifestSpy.mockImplementation(
|
||||
() => <tc.IToolRelease[]>nodeTestManifest
|
||||
);
|
||||
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||
|
||||
getJsonSpy.mockImplementation(url => {
|
||||
getJsonSpy.mockImplementation((url: any) => {
|
||||
let res: any;
|
||||
if (url.includes('/rc')) {
|
||||
res = <INodeVersion[]>nodeTestDistRc;
|
||||
@@ -107,28 +246,18 @@ describe('setup-node', () => {
|
||||
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
logSpy = jest.spyOn(core, 'info');
|
||||
dbgSpy = jest.spyOn(core, 'debug');
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
logSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
process.stderr.write('log:' + line + '\n');
|
||||
});
|
||||
dbgSpy.mockImplementation(msg => {
|
||||
// uncomment to see debug output
|
||||
// process.stderr.write(msg + '\n');
|
||||
});
|
||||
warningSpy.mockImplementation(msg => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + msg + '\n');
|
||||
});
|
||||
logSpy = core.info as jest.Mock;
|
||||
dbgSpy = core.debug as jest.Mock;
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
addPathSpy = core.addPath as jest.Mock;
|
||||
setFailedSpy = core.setFailed as jest.Mock;
|
||||
cnSpy.mockImplementation(() => true);
|
||||
logSpy.mockImplementation(() => {});
|
||||
dbgSpy.mockImplementation(() => {});
|
||||
warningSpy.mockImplementation(() => {});
|
||||
|
||||
// @actions/exec
|
||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||
});
|
||||
|
||||
@@ -156,7 +285,7 @@ describe('setup-node', () => {
|
||||
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
||||
os.platform = platform;
|
||||
os.arch = 'x64';
|
||||
const versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
|
||||
const versions: IToolRelease[] | null = await tc.getManifestFromRepo(
|
||||
'actions',
|
||||
'node-versions',
|
||||
'mocktoken'
|
||||
@@ -187,7 +316,7 @@ describe('setup-node', () => {
|
||||
it('finds version in cache with stable not supplied', async () => {
|
||||
inputs['node-version'] = '12';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
@@ -199,14 +328,14 @@ describe('setup-node', () => {
|
||||
it('finds version in cache and adds it to the path', async () => {
|
||||
inputs['node-version'] = '12';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
await main.run();
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('handles unhandled find error and reports error', async () => {
|
||||
@@ -219,7 +348,7 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
//--------------------------------------------------
|
||||
@@ -247,7 +376,7 @@ describe('setup-node', () => {
|
||||
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
||||
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
whichSpy.mockImplementation(cmd => {
|
||||
whichSpy.mockImplementation((cmd: any) => {
|
||||
return `some/${cmd}/path`;
|
||||
});
|
||||
|
||||
@@ -278,7 +407,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('falls back to a version from node dist from mirror', async () => {
|
||||
@@ -314,7 +443,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(exSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('falls back to a version from node dist', async () => {
|
||||
@@ -348,7 +477,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(exSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('does not find a version that does not exist', async () => {
|
||||
@@ -367,8 +496,8 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -390,7 +519,7 @@ describe('setup-node', () => {
|
||||
});
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('reports when download failed but version exists', async () => {
|
||||
@@ -583,7 +712,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('fallback to dist if manifest is not available', async () => {
|
||||
@@ -626,7 +755,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -668,9 +797,7 @@ describe('setup-node', () => {
|
||||
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -728,9 +855,7 @@ describe('setup-node', () => {
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -750,8 +875,8 @@ describe('setup-node', () => {
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to parse LTS alias for Node version 'lts/'${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to parse LTS alias for Node version 'lts/'`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -774,8 +899,8 @@ describe('setup-node', () => {
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find LTS release 'unknown' for Node version 'lts/unknown'.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find LTS release 'unknown' for Node version 'lts/unknown'.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -799,9 +924,7 @@ describe('setup-node', () => {
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to download manifest${osm.EOL}`
|
||||
);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith('Unable to download manifest');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -871,7 +994,6 @@ describe('setup-node', () => {
|
||||
darwin: 'darwin',
|
||||
win32: 'win'
|
||||
}[os.platform];
|
||||
|
||||
inputs['node-version'] = version;
|
||||
inputs['architecture'] = arch;
|
||||
inputs['token'] = 'faketoken';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import tscMatcher from '../.github/tsc.json';
|
||||
import {describe, it, expect} from '@jest/globals';
|
||||
import tscMatcher from '../.github/tsc.json' with {type: 'json'};
|
||||
|
||||
describe('problem matcher tests', () => {
|
||||
it('tsc: matches TypeScript "pretty" error message', () => {
|
||||
|
||||
+178
-84
@@ -1,46 +1,152 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as cache from '@actions/cache';
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import cp from 'child_process';
|
||||
import osm from 'os';
|
||||
import path from 'path';
|
||||
import * as main from '../src/main';
|
||||
import * as auth from '../src/authutil';
|
||||
import {INodeVersion} from '../src/distributions/base-models';
|
||||
|
||||
import nodeTestDist from './data/node-dist-index.json';
|
||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||
import nodeTestDistRc from './data/node-rc-index.json';
|
||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
notice: jest.fn(),
|
||||
setFailed: jest.fn(),
|
||||
setOutput: jest.fn(),
|
||||
getInput: jest.fn(),
|
||||
getBooleanInput: jest.fn(),
|
||||
getMultilineInput: jest.fn(),
|
||||
addPath: jest.fn(),
|
||||
exportVariable: jest.fn(),
|
||||
saveState: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
setSecret: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn(),
|
||||
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||
toPlatformPath: jest.fn((p: string) => p),
|
||||
toWin32Path: jest.fn((p: string) => p),
|
||||
toPosixPath: jest.fn((p: string) => p)
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/io', () => ({
|
||||
which: jest.fn(),
|
||||
mkdirP: jest.fn(),
|
||||
rmRF: jest.fn(),
|
||||
mv: jest.fn(),
|
||||
cp: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||
find: jest.fn(),
|
||||
findAllVersions: jest.fn(),
|
||||
downloadTool: jest.fn(),
|
||||
extractTar: jest.fn(),
|
||||
extractZip: jest.fn(),
|
||||
extractXar: jest.fn(),
|
||||
extract7z: jest.fn(),
|
||||
cacheDir: jest.fn(),
|
||||
cacheFile: jest.fn(),
|
||||
getManifestFromRepo: jest.fn(),
|
||||
findFromManifest: jest.fn(),
|
||||
HTTPError: class HTTPError extends Error {
|
||||
readonly httpStatusCode: number;
|
||||
constructor(httpStatusCode?: number) {
|
||||
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||
this.httpStatusCode = httpStatusCode ?? 0;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const _mockGetJson = jest.fn();
|
||||
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||
HttpClient: jest.fn().mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}))
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/exec', () => ({
|
||||
exec: jest.fn(),
|
||||
getExecOutput: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/cache', () => ({
|
||||
saveCache: jest.fn(),
|
||||
restoreCache: jest.fn(),
|
||||
isFeatureAvailable: jest.fn()
|
||||
}));
|
||||
|
||||
const realAuth = await import('../src/authutil.js');
|
||||
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||
...realAuth,
|
||||
configAuthentication: jest.fn()
|
||||
}));
|
||||
|
||||
// Dynamic imports after mocking
|
||||
const core = await import('@actions/core');
|
||||
const io = await import('@actions/io');
|
||||
const tc = await import('@actions/tool-cache');
|
||||
const httpm = await import('@actions/http-client');
|
||||
const exec = await import('@actions/exec');
|
||||
const cache = await import('@actions/cache');
|
||||
const main = await import('../src/main.js');
|
||||
const auth = await import('../src/authutil.js');
|
||||
|
||||
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeTestDistNightly} = await import(
|
||||
'./data/node-nightly-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||
with: {type: 'json'}
|
||||
});
|
||||
const {default: nodeV8CanaryTestDist} = await import(
|
||||
'./data/v8-canary-dist-index.json',
|
||||
{with: {type: 'json'}}
|
||||
);
|
||||
|
||||
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||
|
||||
describe('setup-node', () => {
|
||||
let inputs = {} as any;
|
||||
let os = {} as any;
|
||||
|
||||
let inSpy: jest.SpyInstance;
|
||||
let findSpy: jest.SpyInstance;
|
||||
let findAllVersionsSpy: jest.SpyInstance;
|
||||
let cnSpy: jest.SpyInstance;
|
||||
let logSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let platSpy: jest.SpyInstance;
|
||||
let archSpy: jest.SpyInstance;
|
||||
let dlSpy: jest.SpyInstance;
|
||||
let exSpy: jest.SpyInstance;
|
||||
let cacheSpy: jest.SpyInstance;
|
||||
let dbgSpy: jest.SpyInstance;
|
||||
let whichSpy: jest.SpyInstance;
|
||||
let existsSpy: jest.SpyInstance;
|
||||
let mkdirpSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let isCacheActionAvailable: jest.SpyInstance;
|
||||
let getExecOutputSpy: jest.SpyInstance;
|
||||
let getJsonSpy: jest.SpyInstance;
|
||||
let inSpy: jest.Mock;
|
||||
let findSpy: jest.Mock;
|
||||
let findAllVersionsSpy: jest.Mock;
|
||||
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||
let logSpy: jest.Mock;
|
||||
let warningSpy: jest.Mock;
|
||||
let addPathSpy: jest.Mock;
|
||||
let setFailedSpy: jest.Mock;
|
||||
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||
let dlSpy: jest.Mock;
|
||||
let exSpy: jest.Mock;
|
||||
let cacheSpy: jest.Mock;
|
||||
let dbgSpy: jest.Mock;
|
||||
let whichSpy: jest.Mock;
|
||||
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||
let mkdirpSpy: jest.Mock;
|
||||
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||
let authSpy: jest.Mock;
|
||||
let isCacheActionAvailable: jest.Mock;
|
||||
let getExecOutputSpy: jest.Mock;
|
||||
let getJsonSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
@@ -48,8 +154,8 @@ describe('setup-node', () => {
|
||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||
inputs = {};
|
||||
inSpy = jest.spyOn(core, 'getInput');
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy = core.getInput as jest.Mock;
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
// node
|
||||
os = {};
|
||||
@@ -60,30 +166,33 @@ describe('setup-node', () => {
|
||||
execSpy = jest.spyOn(cp, 'execSync');
|
||||
|
||||
// @actions/tool-cache
|
||||
findSpy = jest.spyOn(tc, 'find');
|
||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||
exSpy = jest.spyOn(tc, 'extractTar');
|
||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
|
||||
findSpy = tc.find as jest.Mock;
|
||||
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||
dlSpy = tc.downloadTool as jest.Mock;
|
||||
exSpy = tc.extractTar as jest.Mock;
|
||||
cacheSpy = tc.cacheDir as jest.Mock;
|
||||
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist') as jest.Mock;
|
||||
|
||||
// http-client
|
||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||
getJsonSpy = _mockGetJson;
|
||||
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||
getJson: _mockGetJson
|
||||
}));
|
||||
|
||||
// io
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
whichSpy = io.which as jest.Mock;
|
||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||
|
||||
// @actions/tool-cache
|
||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||
isCacheActionAvailable.mockImplementation(() => false);
|
||||
|
||||
// disable authentication portion for installer tests
|
||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||
authSpy = auth.configAuthentication as jest.Mock;
|
||||
authSpy.mockImplementation(() => {});
|
||||
|
||||
getJsonSpy.mockImplementation(url => {
|
||||
getJsonSpy.mockImplementation((url: any) => {
|
||||
let res: any;
|
||||
if (url.includes('/rc')) {
|
||||
res = <INodeVersion[]>nodeTestDistRc;
|
||||
@@ -98,28 +207,18 @@ describe('setup-node', () => {
|
||||
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
logSpy = jest.spyOn(core, 'info');
|
||||
dbgSpy = jest.spyOn(core, 'debug');
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
cnSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('write:' + line + '\n');
|
||||
});
|
||||
logSpy.mockImplementation(line => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + line + '\n');
|
||||
});
|
||||
dbgSpy.mockImplementation(msg => {
|
||||
// uncomment to see debug output
|
||||
// process.stderr.write(msg + '\n');
|
||||
});
|
||||
warningSpy.mockImplementation(msg => {
|
||||
// uncomment to debug
|
||||
// process.stderr.write('log:' + msg + '\n');
|
||||
});
|
||||
logSpy = core.info as jest.Mock;
|
||||
dbgSpy = core.debug as jest.Mock;
|
||||
warningSpy = core.warning as jest.Mock;
|
||||
addPathSpy = core.addPath as jest.Mock;
|
||||
setFailedSpy = core.setFailed as jest.Mock;
|
||||
cnSpy.mockImplementation(() => true);
|
||||
logSpy.mockImplementation(() => {});
|
||||
dbgSpy.mockImplementation(() => {});
|
||||
warningSpy.mockImplementation(() => {});
|
||||
|
||||
// @actions/exec
|
||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
||||
});
|
||||
|
||||
@@ -152,7 +251,7 @@ describe('setup-node', () => {
|
||||
it('finds version in cache with stable not supplied', async () => {
|
||||
inputs['node-version'] = '12.0.0-rc.1';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
@@ -164,14 +263,14 @@ describe('setup-node', () => {
|
||||
it('finds version in cache and adds it to the path', async () => {
|
||||
inputs['node-version'] = '12.0.0-rc.1';
|
||||
|
||||
inSpy.mockImplementation(name => inputs[name]);
|
||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||
|
||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||
findSpy.mockImplementation(() => toolPath);
|
||||
await main.run();
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('handles unhandled find error and reports error', async () => {
|
||||
@@ -184,7 +283,7 @@ describe('setup-node', () => {
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('falls back to a version from node dist', async () => {
|
||||
@@ -212,7 +311,7 @@ describe('setup-node', () => {
|
||||
expect(exSpy).toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Done');
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
|
||||
it('does not find a version that does not exist', async () => {
|
||||
@@ -225,8 +324,8 @@ describe('setup-node', () => {
|
||||
findSpy.mockImplementation(() => '');
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -247,7 +346,7 @@ describe('setup-node', () => {
|
||||
});
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||
});
|
||||
|
||||
it('acquires specified architecture of node', async () => {
|
||||
@@ -263,7 +362,6 @@ describe('setup-node', () => {
|
||||
darwin: 'darwin',
|
||||
win32: 'win'
|
||||
}[os.platform];
|
||||
|
||||
inputs['node-version'] = version;
|
||||
inputs['architecture'] = arch;
|
||||
inputs['token'] = 'faketoken';
|
||||
@@ -334,9 +432,7 @@ describe('setup-node', () => {
|
||||
// assert
|
||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -370,9 +466,7 @@ describe('setup-node', () => {
|
||||
|
||||
// assert
|
||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -391,8 +485,8 @@ describe('setup-node', () => {
|
||||
await main.run();
|
||||
|
||||
// assert
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user