2026-07-14 08:08:27 +05:30
import {
jest ,
describe ,
it ,
expect ,
beforeEach ,
afterEach ,
afterAll
} from '@jest/globals' ;
import { fileURLToPath } from 'url' ;
2020-05-19 09:25:54 -04:00
import fs from 'fs' ;
import cp from 'child_process' ;
2022-11-17 14:35:58 +01:00
import osm from 'os' ;
2020-05-19 09:25:54 -04:00
import path from 'path' ;
2026-07-14 08:08:27 +05:30
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' ;
2020-05-19 09:25:54 -04:00
describe ( 'setup-node' , () => {
2026-07-14 08:08:27 +05:30
let build : InstanceType < typeof OfficialBuilds >;
2020-05-19 09:25:54 -04:00
let inputs = {} as any ;
let os = {} as any ;
2026-07-14 08:08:27 +05:30
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 ;
2020-05-19 09:25:54 -04:00
beforeEach (() => {
// @actions/core
2020-10-01 12:03:22 -04:00
console . log ( '::stop-commands::stoptoken' ); // Disable executing of runner commands when running tests in actions
process . env [ 'GITHUB_PATH' ] = '' ; // Stub out ENV file functionality so we can verify it writes to standard out
2022-10-13 11:11:08 +01:00
process . env [ 'GITHUB_OUTPUT' ] = '' ; // Stub out ENV file functionality so we can verify it writes to standard out
2020-05-19 09:25:54 -04:00
inputs = {};
2026-07-14 08:08:27 +05:30
inSpy = core . getInput as jest . Mock ;
inSpy . mockImplementation (( name : any ) => inputs [ name ]);
2020-05-19 09:25:54 -04:00
// node
os = {};
platSpy = jest . spyOn ( osm , 'platform' );
platSpy . mockImplementation (() => os [ 'platform' ]);
archSpy = jest . spyOn ( osm , 'arch' );
archSpy . mockImplementation (() => os [ 'arch' ]);
execSpy = jest . spyOn ( cp , 'execSync' );
// @actions/tool-cache
2026-07-14 08:08:27 +05:30
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 ;
}
);
2020-05-19 09:25:54 -04:00
2022-11-17 14:35:58 +01:00
// http-client
2026-07-14 08:08:27 +05:30
getJsonSpy = _mockGetJson ;
( httpm . HttpClient as jest . Mock ). mockImplementation (() => ({
getJson : _mockGetJson
}));
2022-11-17 14:35:58 +01:00
2020-05-19 09:25:54 -04:00
// io
2026-07-14 08:08:27 +05:30
whichSpy = io . which as jest . Mock ;
2020-05-19 09:25:54 -04:00
existsSpy = jest . spyOn ( fs , 'existsSync' );
2026-07-14 08:08:27 +05:30
mkdirpSpy = io . mkdirP as jest . Mock ;
2020-05-19 09:25:54 -04:00
2022-03-31 21:10:37 +02:00
// @actions/tool-cache
2026-07-14 08:08:27 +05:30
isCacheActionAvailable = cache . isFeatureAvailable as jest . Mock ;
2022-03-31 21:10:37 +02:00
2020-05-19 09:25:54 -04:00
// disable authentication portion for installer tests
2026-07-14 08:08:27 +05:30
authSpy = auth . configAuthentication as jest . Mock ;
2020-05-19 09:25:54 -04:00
authSpy . mockImplementation (() => {});
// gets
2026-07-14 08:08:27 +05:30
getManifestSpy . mockImplementation (() => < IToolRelease [] > nodeTestManifest );
2022-11-17 14:35:58 +01:00
2026-07-14 08:08:27 +05:30
getJsonSpy . mockImplementation (( url : any ) => {
2022-11-17 14:35:58 +01:00
let res : any ;
if ( url . includes ( '/rc' )) {
2023-03-08 10:47:38 +02:00
res = < INodeVersion [] > nodeTestDistRc ;
2022-11-17 14:35:58 +01:00
} else if ( url . includes ( '/nightly' )) {
2023-03-08 10:47:38 +02:00
res = < INodeVersion [] > nodeTestDistNightly ;
2022-11-17 14:35:58 +01:00
} else {
2023-03-08 10:47:38 +02:00
res = < INodeVersion [] > nodeTestDist ;
2022-11-17 14:35:58 +01:00
}
return { result : res };
});
2020-05-19 09:25:54 -04:00
// writes
cnSpy = jest . spyOn ( process . stdout , 'write' );
2026-07-14 08:08:27 +05:30
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 (() => {});
2022-07-01 01:27:37 +02:00
// @actions/exec
2026-07-14 08:08:27 +05:30
getExecOutputSpy = exec . getExecOutput as jest . Mock ;
2026-07-15 04:25:44 +05:30
getExecOutputSpy . mockImplementation ( async () => ({
stdout : 'v16.15.0' ,
stderr : '' ,
exitCode : 0
}));
2020-05-19 09:25:54 -04:00
});
afterEach (() => {
jest . resetAllMocks ();
jest . clearAllMocks ();
//jest.restoreAllMocks();
});
2020-10-01 12:03:22 -04:00
afterAll ( async () => {
console . log ( '::stoptoken::' ); // Re-enable executing of runner commands when running tests in actions
2021-11-29 14:48:31 +05:30
jest . restoreAllMocks ();
2020-10-01 12:03:22 -04:00
}, 100000 );
2020-05-19 09:25:54 -04:00
//--------------------------------------------------
// Manifest find tests
//--------------------------------------------------
2022-05-18 00:50:05 -07:00
it . each ([
[ '12.16.2' , 'darwin' , '12.16.2' , 'Erbium' ],
[ '12' , 'linux' , '12.16.2' , 'Erbium' ],
[ '10' , 'win32' , '10.20.1' , 'Dubnium' ],
[ '*' , 'linux' , '14.0.0' , 'Fermium' ]
])(
'can find %s from manifest on %s' ,
async ( versionSpec , platform , expectedVersion , expectedLts ) => {
os . platform = platform ;
os . arch = 'x64' ;
2026-07-14 08:08:27 +05:30
const versions : IToolRelease [] | null = await tc . getManifestFromRepo (
2022-05-18 00:50:05 -07:00
'actions' ,
'node-versions' ,
'mocktoken'
);
expect ( versions ). toBeDefined ();
2023-03-08 10:47:38 +02:00
const match = await tc . findFromManifest ( versionSpec , true , versions );
2022-05-18 00:50:05 -07:00
expect ( match ). toBeDefined ();
expect ( match ? . version ). toBe ( expectedVersion );
expect (( match as any ). lts ). toBe ( expectedLts );
}
);
2020-05-19 09:25:54 -04:00
//--------------------------------------------------
// Found in cache tests
//--------------------------------------------------
2021-08-04 17:00:35 +03:00
it ( 'finds version in cache with stable true' , async () => {
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = '12' ;
inputs . stable = 'true' ;
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
2020-05-19 09:25:54 -04:00
findSpy . mockImplementation (() => toolPath );
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
2019-08-03 21:49:54 -04:00
});
2021-08-04 17:00:35 +03:00
it ( 'finds version in cache with stable not supplied' , async () => {
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = '12' ;
2026-07-14 08:08:27 +05:30
inSpy . mockImplementation (( name : any ) => inputs [ name ]);
2020-05-19 09:25:54 -04:00
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
2020-05-19 09:25:54 -04:00
findSpy . mockImplementation (() => toolPath );
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
2019-08-03 21:49:54 -04:00
});
2021-08-04 17:00:35 +03:00
it ( 'finds version in cache and adds it to the path' , async () => {
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = '12' ;
2026-07-14 08:08:27 +05:30
inSpy . mockImplementation (( name : any ) => inputs [ name ]);
2020-05-19 09:25:54 -04:00
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
2020-05-19 09:25:54 -04:00
findSpy . mockImplementation (() => toolPath );
await main . run ();
2023-03-08 10:47:38 +02:00
const expPath = path . join ( toolPath , 'bin' );
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2019-08-03 21:49:54 -04:00
});
2020-05-19 09:25:54 -04:00
it ( 'handles unhandled find error and reports error' , async () => {
2023-03-08 10:47:38 +02:00
const errMsg = 'unhandled error message' ;
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = '12' ;
findSpy . mockImplementation (() => {
throw new Error ( errMsg );
});
await main . run ();
2026-07-14 08:08:27 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith ( errMsg );
2020-05-19 09:25:54 -04:00
});
2021-06-17 17:51:02 +02:00
//--------------------------------------------------
// Manifest tests
//--------------------------------------------------
2020-05-19 09:25:54 -04:00
it ( 'downloads a version from a manifest match' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is in the manifest
2023-03-08 10:47:38 +02:00
const versionSpec = '12.16.2' ;
const resolvedVersion = versionSpec ;
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'token' ] = 'faketoken' ;
2023-03-08 10:47:38 +02:00
const expectedUrl =
2022-06-02 03:30:33 -07:00
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz' ;
2020-05-19 09:25:54 -04:00
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.2/x64' );
2020-05-19 09:25:54 -04:00
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
2026-07-14 08:08:27 +05:30
whichSpy . mockImplementation (( cmd : any ) => {
2024-02-07 05:42:16 +01:00
return `some/ ${ cmd } /path` ;
});
2026-07-15 04:25:44 +05:30
getExecOutputSpy . mockImplementation ( async ( cmd : string ) => ({
stdout :
cmd === 'node'
? `v ${ resolvedVersion } `
: cmd === 'npm'
? '11.12.1'
: '4.17.1' ,
stderr : '' ,
exitCode : 0
}));
2020-05-19 09:25:54 -04:00
await main . run ();
2023-03-08 10:47:38 +02:00
const expPath = path . join ( toolPath , 'bin' );
2020-05-19 09:25:54 -04:00
2022-10-03 17:02:04 +03:00
expect ( getExecOutputSpy ). toHaveBeenCalledWith (
'node' ,
[ '--version' ],
expect . anything ()
);
expect ( getExecOutputSpy ). toHaveBeenCalledWith (
'npm' ,
[ '--version' ],
expect . anything ()
);
expect ( getExecOutputSpy ). toHaveBeenCalledWith (
'yarn' ,
[ '--version' ],
expect . anything ()
);
2020-05-19 09:25:54 -04:00
expect ( dlSpy ). toHaveBeenCalled ();
expect ( exSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
2020-09-03 07:50:29 -05:00
`Acquiring ${ resolvedVersion } - ${ os . arch } from ${ expectedUrl } `
2020-05-19 09:25:54 -04:00
);
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2020-05-19 09:25:54 -04:00
});
2025-04-02 17:49:47 +02:00
it ( 'falls back to a version from node dist from mirror' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0' ;
const mirror = 'https://my_mirror_url' ;
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'token' ] = 'faketoken' ;
inputs [ 'mirror' ] = mirror ;
inputs [ 'mirror-token' ] = 'faketoken' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
const toolPath = path . normalize ( '/cache/node/11.15.0/x64' );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
const expPath = path . join ( toolPath , 'bin' );
expect ( getManifestSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
expect ( logSpy ). toHaveBeenCalledWith (
`Not found in manifest. Falling back to download directly from ${ mirror } `
);
expect ( dlSpy ). toHaveBeenCalled ();
expect ( exSpy ). toHaveBeenCalled ();
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2025-04-02 17:49:47 +02:00
});
2020-05-19 09:25:54 -04:00
it ( 'falls back to a version from node dist' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is not in the manifest but is in node dist
2023-03-08 10:47:38 +02:00
const versionSpec = '11.15.0' ;
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'token' ] = 'faketoken' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
2023-01-05 13:16:21 +01:00
const toolPath = path . normalize ( '/cache/node/11.15.0/x64' );
2020-05-19 09:25:54 -04:00
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
2023-01-05 13:16:21 +01:00
const expPath = path . join ( toolPath , 'bin' );
2020-05-19 09:25:54 -04:00
2023-01-05 13:16:21 +01:00
expect ( getManifestSpy ). toHaveBeenCalled ();
2020-05-19 09:25:54 -04:00
expect ( logSpy ). toHaveBeenCalledWith (
2023-01-05 13:16:21 +01:00
`Attempting to download ${ versionSpec } ...`
2020-05-19 09:25:54 -04:00
);
expect ( logSpy ). toHaveBeenCalledWith (
2023-01-05 13:16:21 +01:00
'Not found in manifest. Falling back to download directly from Node'
2020-05-19 09:25:54 -04:00
);
2023-01-05 13:16:21 +01:00
expect ( dlSpy ). toHaveBeenCalled ();
expect ( exSpy ). toHaveBeenCalled ();
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2020-05-19 09:25:54 -04:00
});
it ( 'does not find a version that does not exist' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
2023-03-08 10:47:38 +02:00
const versionSpec = '9.99.9' ;
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = versionSpec ;
findSpy . mockImplementation (() => '' );
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith (
2023-01-05 13:16:21 +01:00
'Not found in manifest. Falling back to download directly from Node'
2020-05-19 09:25:54 -04:00
);
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
2026-07-14 08:08:27 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Unable to find Node version ' ${ versionSpec } ' for platform ${ os . platform } and architecture ${ os . arch } .`
2020-05-19 09:25:54 -04:00
);
});
it ( 'reports a failed download' , async () => {
2023-03-08 10:47:38 +02:00
const errMsg = 'unhandled download message' ;
2020-05-19 09:25:54 -04:00
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is in the manifest
2023-03-08 10:47:38 +02:00
const versionSpec = '12.16.2' ;
const resolvedVersion = versionSpec ;
2020-05-19 09:25:54 -04:00
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'token' ] = 'faketoken' ;
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation (() => {
throw new Error ( errMsg );
});
await main . run ();
2026-07-14 08:08:27 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith ( errMsg );
2019-08-03 21:49:54 -04:00
});
2020-06-29 21:56:37 +03:00
2023-10-19 13:43:56 +02:00
it ( 'reports when download failed but version exists' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0' ;
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'token' ] = 'faketoken' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementationOnce ( async () => {
throw new tc . HTTPError ( 404 );
});
await main . run ();
expect ( getManifestSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
expect ( logSpy ). toHaveBeenCalledWith (
'Not found in manifest. Falling back to download directly from Node'
);
expect ( dlSpy ). toHaveBeenCalled ();
2023-10-19 17:12:39 +02:00
expect ( warningSpy ). toHaveBeenCalledWith (
2023-10-19 13:43:56 +02:00
`Node version ${ versionSpec } for platform ${ os . platform } and architecture ${ os . arch } was found but failed to download. ` +
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
'To resolve this issue you may either fall back to the older version or try again later.'
);
});
2021-11-29 14:48:31 +05:30
it ( 'acquires specified architecture of node' , async () => {
2020-09-03 10:15:28 -05:00
for ( const { arch , version , osSpec } of [
{ arch : 'x86' , version : '12.16.2' , osSpec : 'win32' },
{ arch : 'x86' , version : '14.0.0' , osSpec : 'win32' }
2020-09-03 08:47:25 -05:00
]) {
2020-09-03 10:15:28 -05:00
os . platform = osSpec ;
os . arch = arch ;
const fileExtension = os . platform === 'win32' ? '7z' : 'tar.gz' ;
2020-09-03 08:47:25 -05:00
const platform = {
linux : 'linux' ,
darwin : 'darwin' ,
win32 : 'win'
2020-09-03 10:15:28 -05:00
}[ os . platform ];
inputs [ 'node-version' ] = version ;
2020-12-08 16:15:38 -06:00
inputs [ 'architecture' ] = arch ;
2020-09-03 10:15:28 -05:00
inputs [ 'token' ] = 'faketoken' ;
2023-03-08 10:47:38 +02:00
const expectedUrl =
2020-09-03 10:15:28 -05:00
arch === 'x64'
? `https://github.com/actions/node-versions/releases/download/ ${ version } /node- ${ version } - ${ platform } - ${ arch } .zip`
: `https://nodejs.org/dist/v ${ version } /node-v ${ version } - ${ platform } - ${ arch } . ${ fileExtension } ` ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( `/cache/node/ ${ version } / ${ arch } ` );
2020-09-03 10:15:28 -05:00
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
expect ( dlSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
`Acquiring ${ version } - ${ arch } from ${ expectedUrl } `
);
2020-09-03 08:47:25 -05:00
}
}, 100000 );
2020-06-29 21:56:37 +03:00
describe ( 'check-latest flag' , () => {
2021-08-04 17:00:35 +03:00
it ( 'use local version and dont check manifest if check-latest is not specified' , async () => {
2020-06-29 21:56:37 +03:00
os . platform = 'linux' ;
os . arch = 'x64' ;
inputs [ 'node-version' ] = '12' ;
inputs [ 'check-latest' ] = 'false' ;
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
findSpy . mockReturnValue ( toolPath );
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
expect ( logSpy ). not . toHaveBeenCalledWith (
'Attempt to resolve the latest version from manifest...'
);
2021-06-22 17:52:06 +02:00
expect ( dbgSpy ). not . toHaveBeenCalledWith ( 'No manifest cached' );
2021-06-22 16:11:44 +02:00
expect ( dbgSpy ). not . toHaveBeenCalledWith (
2021-06-22 17:52:06 +02:00
'Getting manifest from actions/node-versions@main'
2021-06-22 16:11:44 +02:00
);
2020-06-29 21:56:37 +03:00
});
2021-08-04 17:00:35 +03:00
it ( 'check latest version and resolve it from local cache' , async () => {
2020-06-29 21:56:37 +03:00
os . platform = 'linux' ;
os . arch = 'x64' ;
inputs [ 'node-version' ] = '12' ;
inputs [ 'check-latest' ] = 'true' ;
const toolPath = path . normalize ( '/cache/node/12.16.2/x64' );
findSpy . mockReturnValue ( toolPath );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve the latest version from manifest...'
);
2021-06-22 17:52:06 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith ( 'No manifest cached' );
2021-06-22 16:11:44 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith (
2021-06-22 17:52:06 +02:00
'Getting manifest from actions/node-versions@main'
2021-06-22 16:11:44 +02:00
);
2020-06-29 21:56:37 +03:00
expect ( logSpy ). toHaveBeenCalledWith ( "Resolved as '12.16.2'" );
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
});
it ( 'check latest version and install it from manifest' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
inputs [ 'node-version' ] = '12' ;
inputs [ 'check-latest' ] = 'true' ;
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
const toolPath = path . normalize ( '/cache/node/12.16.2/x64' );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
const expectedUrl =
2022-06-02 03:30:33 -07:00
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz' ;
2020-06-29 21:56:37 +03:00
await main . run ();
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve the latest version from manifest...'
);
2021-06-22 17:52:06 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith ( 'No manifest cached' );
2021-06-22 16:11:44 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith (
2021-06-22 17:52:06 +02:00
'Getting manifest from actions/node-versions@main'
2021-06-22 16:11:44 +02:00
);
2020-06-29 21:56:37 +03:00
expect ( logSpy ). toHaveBeenCalledWith ( "Resolved as '12.16.2'" );
expect ( logSpy ). toHaveBeenCalledWith (
2020-09-03 07:50:29 -05:00
`Acquiring 12.16.2 - ${ os . arch } from ${ expectedUrl } `
2020-06-29 21:56:37 +03:00
);
expect ( logSpy ). toHaveBeenCalledWith ( 'Extracting ...' );
});
it ( 'fallback to dist if version if not found in manifest' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is not in the manifest but is in node dist
2023-03-08 10:47:38 +02:00
const versionSpec = '11' ;
2020-06-29 21:56:37 +03:00
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'check-latest' ] = 'true' ;
inputs [ 'token' ] = 'faketoken' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/11.11.0/x64' );
2020-06-29 21:56:37 +03:00
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
2023-03-08 10:47:38 +02:00
const expPath = path . join ( toolPath , 'bin' );
2020-06-29 21:56:37 +03:00
expect ( dlSpy ). toHaveBeenCalled ();
expect ( exSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve the latest version from manifest...'
);
2021-06-22 17:52:06 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith ( 'No manifest cached' );
2021-06-22 16:11:44 +02:00
expect ( dbgSpy ). toHaveBeenCalledWith (
2021-06-22 17:52:06 +02:00
'Getting manifest from actions/node-versions@main'
2021-06-22 16:11:44 +02:00
);
2020-06-29 21:56:37 +03:00
expect ( logSpy ). toHaveBeenCalledWith (
`Failed to resolve version ${ versionSpec } from manifest`
);
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2020-06-29 21:56:37 +03:00
});
it ( 'fallback to dist if manifest is not available' , async () => {
os . platform = 'linux' ;
os . arch = 'x64' ;
// a version which is not in the manifest but is in node dist
2023-03-08 10:47:38 +02:00
const versionSpec = '12' ;
2020-06-29 21:56:37 +03:00
inputs [ 'node-version' ] = versionSpec ;
inputs [ 'check-latest' ] = 'true' ;
inputs [ 'token' ] = 'faketoken' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
getManifestSpy . mockImplementation (() => {
throw new Error ( 'Unable to download manifest' );
});
dlSpy . mockImplementation ( async () => '/some/temp/path' );
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.11.0/x64' );
2020-06-29 21:56:37 +03:00
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
2023-03-08 10:47:38 +02:00
const expPath = path . join ( toolPath , 'bin' );
2020-06-29 21:56:37 +03:00
expect ( dlSpy ). toHaveBeenCalled ();
expect ( exSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve the latest version from manifest...'
);
expect ( logSpy ). toHaveBeenCalledWith (
'Unable to resolve version from manifest...'
);
expect ( logSpy ). toHaveBeenCalledWith (
`Failed to resolve version ${ versionSpec } from manifest`
);
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ versionSpec } ...`
);
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( expPath );
2026-07-15 04:25:44 +05:30
}, 10000 );
2020-06-29 21:56:37 +03:00
});
2021-06-17 17:51:02 +02:00
describe ( 'LTS version' , () => {
2021-06-30 09:27:49 +02:00
beforeEach (() => {
2021-06-17 17:51:02 +02:00
os . platform = 'linux' ;
os . arch = 'x64' ;
2021-06-30 09:27:49 +02:00
inputs . stable = 'true' ;
});
2021-06-17 17:51:02 +02:00
2022-06-02 03:30:33 -07:00
it . each ([
[ 'erbium' , '12.16.2' ],
[ '*' , '14.0.0' ],
[ '-1' , '12.16.2' ]
])(
'find latest LTS version and resolve it from local cache (lts/%s)' ,
async ( lts , expectedVersion ) => {
// arrange
inputs [ 'node-version' ] = `lts/ ${ lts } ` ;
const toolPath = path . normalize ( `/cache/node/ ${ expectedVersion } /x64` );
findSpy . mockReturnValue ( toolPath );
// act
await main . run ();
// assert
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve LTS alias from manifest...'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
'Getting manifest from actions/node-versions@main'
);
expect ( dbgSpy ). not . toHaveBeenCalledWith ( 'No manifest cached' );
expect ( dbgSpy ). toHaveBeenCalledWith (
`LTS alias ' ${ lts } ' for Node version 'lts/ ${ lts } '`
);
expect ( dbgSpy ). toHaveBeenCalledWith (
`Found LTS release ' ${ expectedVersion } ' for Node version 'lts/ ${ lts } '`
);
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( path . join ( toolPath , 'bin' ));
2022-06-02 03:30:33 -07:00
}
);
2021-06-17 18:34:09 +02:00
2022-06-02 03:30:33 -07:00
it . each ([
[
'erbium' ,
'12.16.2' ,
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
],
[
'*' ,
'14.0.0' ,
'https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-linux-x64.tar.gz'
],
[
'-1' ,
'12.16.2' ,
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
]
])(
'find latest LTS version and install it from manifest (lts/%s)' ,
async ( lts , expectedVersion , expectedUrl ) => {
// arrange
inputs [ 'node-version' ] = `lts/ ${ lts } ` ;
const toolPath = path . normalize ( `/cache/node/ ${ expectedVersion } /x64` );
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
const expectedMajor = expectedVersion . split ( '.' )[ 0 ];
// act
await main . run ();
// assert
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve LTS alias from manifest...'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
'Getting manifest from actions/node-versions@main'
);
expect ( dbgSpy ). not . toHaveBeenCalledWith ( 'No manifest cached' );
expect ( dbgSpy ). toHaveBeenCalledWith (
`LTS alias ' ${ lts } ' for Node version 'lts/ ${ lts } '`
);
expect ( dbgSpy ). toHaveBeenCalledWith (
`Found LTS release ' ${ expectedVersion } ' for Node version 'lts/ ${ lts } '`
);
expect ( logSpy ). toHaveBeenCalledWith (
`Attempting to download ${ expectedMajor } ...`
);
expect ( logSpy ). toHaveBeenCalledWith (
`Acquiring ${ expectedVersion } - ${ os . arch } from ${ expectedUrl } `
);
expect ( logSpy ). toHaveBeenCalledWith ( 'Extracting ...' );
expect ( logSpy ). toHaveBeenCalledWith ( 'Adding to the cache ...' );
2026-07-14 08:08:27 +05:30
expect ( addPathSpy ). toHaveBeenCalledWith ( path . join ( toolPath , 'bin' ));
2022-06-02 03:30:33 -07:00
}
);
2021-06-17 18:53:38 +02:00
2021-06-30 09:34:42 +02:00
it ( 'fail with unable to parse LTS alias (lts/)' , async () => {
2021-06-17 18:53:38 +02:00
// arrange
inputs [ 'node-version' ] = 'lts/' ;
findSpy . mockImplementation (() => '' );
// act
await main . run ();
// assert
2021-06-22 16:11:44 +02:00
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve LTS alias from manifest...'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
'Getting manifest from actions/node-versions@main'
);
2026-07-14 08:08:27 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Unable to parse LTS alias for Node version 'lts/'`
2021-06-22 16:11:44 +02:00
);
2021-06-17 18:53:38 +02:00
});
it ( 'fail to find LTS version (lts/unknown)' , async () => {
// arrange
inputs [ 'node-version' ] = 'lts/unknown' ;
findSpy . mockImplementation (() => '' );
// act
await main . run ();
// assert
2021-06-22 16:11:44 +02:00
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve LTS alias from manifest...'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
'Getting manifest from actions/node-versions@main'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
`LTS alias 'unknown' for Node version 'lts/unknown'`
);
2026-07-14 08:08:27 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Unable to find LTS release 'unknown' for Node version 'lts/unknown'.`
2021-06-22 16:11:44 +02:00
);
2021-06-17 18:53:38 +02:00
});
2021-06-22 15:45:25 +02:00
it ( 'fail if manifest is not available' , async () => {
// arrange
inputs [ 'node-version' ] = 'lts/erbium' ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
getManifestSpy . mockImplementation (() => {
throw new Error ( 'Unable to download manifest' );
});
// act
await main . run ();
// assert
2021-06-22 16:11:44 +02:00
expect ( logSpy ). toHaveBeenCalledWith (
'Attempt to resolve LTS alias from manifest...'
);
expect ( dbgSpy ). toHaveBeenCalledWith (
'Getting manifest from actions/node-versions@main'
);
2026-07-15 04:25:44 +05:30
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Failed to fetch a valid manifest after 3 attempts. Last error: Unable to download manifest`
);
}, 10000 );
2021-06-22 16:11:44 +02:00
});
2022-05-12 17:26:02 +02:00
describe ( 'latest alias syntax' , () => {
it . each ([ 'latest' , 'current' , 'node' ])(
'download the %s version if alias is provided' ,
async inputVersion => {
// Arrange
inputs [ 'node-version' ] = inputVersion ;
os . platform = 'darwin' ;
os . arch = 'x64' ;
findSpy . mockImplementation (() => '' );
getManifestSpy . mockImplementation (() => {
throw new Error ( 'Unable to download manifest' );
});
// Act
await main . run ();
// assert
2026-07-15 04:25:44 +05:30
expect ( logSpy ). toHaveBeenCalledWith (
'Failed to fetch a valid manifest after 3 attempts. Last error: Unable to download manifest'
);
2022-05-12 17:26:02 +02:00
expect ( logSpy ). toHaveBeenCalledWith ( 'getting latest node version...' );
2026-07-15 04:25:44 +05:30
},
10000
2022-05-12 17:26:02 +02:00
);
});
2022-05-19 17:33:13 +02:00
describe ( 'latest alias syntax from cache' , () => {
it . each ([ 'latest' , 'current' , 'node' ])(
'download the %s version if alias is provided' ,
async inputVersion => {
// Arrange
inputs [ 'node-version' ] = inputVersion ;
const expectedVersion = nodeTestDist [ 0 ];
os . platform = 'darwin' ;
os . arch = 'x64' ;
const toolPath = path . normalize (
`/cache/node/ ${ expectedVersion . version } /x64`
);
2023-01-05 13:16:21 +01:00
findSpy . mockImplementation (() => toolPath );
2022-05-19 17:33:13 +02:00
// Act
await main . run ();
// assert
expect ( logSpy ). toHaveBeenCalledWith ( 'getting latest node version...' );
2023-01-05 13:16:21 +01:00
expect ( logSpy ). toHaveBeenCalledWith ( `Found in cache @ ${ toolPath } ` );
2022-05-19 17:33:13 +02:00
}
);
});
2025-04-02 17:49:47 +02:00
it ( 'acquires specified architecture of node from mirror' , async () => {
for ( const { arch , version , osSpec } of [
{ arch : 'x86' , version : '12.16.2' , osSpec : 'win32' },
{ arch : 'x86' , version : '14.0.0' , osSpec : 'win32' }
]) {
os . platform = osSpec ;
os . arch = arch ;
const fileExtension = os . platform === 'win32' ? '7z' : 'tar.gz' ;
const platform = {
linux : 'linux' ,
darwin : 'darwin' ,
win32 : 'win'
}[ os . platform ];
inputs [ 'node-version' ] = version ;
inputs [ 'architecture' ] = arch ;
inputs [ 'token' ] = 'faketoken' ;
inputs [ 'mirror' ] = 'https://my_mirror_url' ;
inputs [ 'mirror-token' ] = 'faketoken' ;
const expectedUrl =
arch === 'x64'
? `https://github.com/actions/node-versions/releases/download/ ${ version } /node- ${ version } - ${ platform } - ${ arch } .zip`
: `https://my_mirror_url/dist/v ${ version } /node-v ${ version } - ${ platform } - ${ arch } . ${ fileExtension } ` ;
// ... but not in the local cache
findSpy . mockImplementation (() => '' );
dlSpy . mockImplementation ( async () => '/some/temp/path' );
const toolPath = path . normalize ( `/cache/node/ ${ version } / ${ arch } ` );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
await main . run ();
expect ( dlSpy ). toHaveBeenCalled ();
expect ( logSpy ). toHaveBeenCalledWith (
`Acquiring ${ version } - ${ arch } from ${ expectedUrl } `
);
}
}, 100000 );
2026-07-15 04:25:44 +05:30
describe ( 'manifest retry and validation' , () => {
beforeEach (() => {
os . platform = 'linux' ;
os . arch = 'x64' ;
inputs [ 'node-version' ] = 'lts/erbium' ;
findSpy . mockImplementation (() => '' );
});
it ( 'retries fetching the manifest and succeeds on a later attempt' , async () => {
let calls = 0 ;
getManifestSpy . mockImplementation (() => {
calls ++ ;
if ( calls < 2 ) {
throw new Error ( 'transient network failure' );
}
return < tc.IToolRelease [] > nodeTestManifest ;
});
dlSpy . mockImplementation ( async () => '/some/temp/path' );
const toolPath = path . normalize ( '/cache/node/12.16.2/x64' );
exSpy . mockImplementation ( async () => '/some/other/temp/path' );
cacheSpy . mockImplementation ( async () => toolPath );
getExecOutputSpy . mockImplementation ( async () => ({
stdout : `v ${ path . basename ( path . dirname ( toolPath )) } \ n` ,
stderr : '' ,
exitCode : 0
}));
await main . run ();
expect ( calls ). toBe ( 2 );
expect ( logSpy ). toHaveBeenCalledWith ( 'Retrying to fetch the manifest...' );
expect ( dbgSpy ). toHaveBeenCalledWith (
`Found LTS release '12.16.2' for Node version 'lts/erbium'`
);
}, 10000 );
it ( 'rejects an empty manifest as invalid and retries' , async () => {
getManifestSpy . mockImplementation (() => []);
await main . run ();
expect ( getManifestSpy ). toHaveBeenCalledTimes ( 3 );
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Failed to fetch a valid manifest after 3 attempts. Last error: The manifest fetched is empty, truncated, or does not contain any valid tool release entries.`
);
}, 10000 );
});
describe ( 'node version verification' , () => {
beforeEach (() => {
os . platform = 'linux' ;
os . arch = 'x64' ;
inputs [ 'node-version' ] = '12' ;
});
it ( 'fails when the installed node version does not match the expected version' , async () => {
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
findSpy . mockReturnValue ( toolPath );
getExecOutputSpy . mockImplementation ( async () => ({
stdout : 'v22.22.3\n' ,
stderr : '' ,
exitCode : 0
}));
await main . run ();
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Node v12.16.1 installation failed, likely due to an incomplete or corrupted download.`
);
});
it ( 'fails when the node executable cannot be invoked' , async () => {
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
findSpy . mockReturnValue ( toolPath );
getExecOutputSpy . mockImplementation ( async () => {
throw new Error ( 'node not found' );
});
await main . run ();
expect ( setFailedSpy ). toHaveBeenCalledWith (
`Node installation failed. Node may not be installed or not on PATH: node not found`
);
});
});
2019-08-03 21:49:54 -04:00
});