2021-06-16 09:52:44 +03:00
import * as core from '@actions/core' ;
2022-03-31 21:10:37 +02:00
import * as cache from '@actions/cache' ;
2021-06-16 09:52:44 +03:00
import path from 'path' ;
import * as utils from '../src/cache-utils' ;
2023-06-21 17:52:17 +02:00
import {
PackageManagerInfo ,
isCacheFeatureAvailable ,
supportedPackageManagers ,
2023-06-27 13:07:43 +02:00
getCommandOutput ,
resetProjectDirectoriesMemoized
2023-06-21 17:52:17 +02:00
} 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' ;
2021-06-16 09:52:44 +03:00
describe ( 'cache-utils' , () => {
const versionYarn1 = '1.2.3' ;
let debugSpy : jest.SpyInstance ;
let getCommandOutputSpy : jest.SpyInstance ;
2022-03-31 21:10:37 +02:00
let isFeatureAvailable : jest.SpyInstance ;
let info : jest.SpyInstance ;
let warningSpy : jest.SpyInstance ;
2023-07-13 14:06:23 +02:00
let fsRealPathSyncSpy : jest.SpyInstance ;
2021-06-16 09:52:44 +03:00
beforeEach (() => {
2023-07-13 14:06:23 +02:00
console . log ( '::stop-commands::stoptoken' );
2021-06-16 09:52:44 +03:00
process . env [ 'GITHUB_WORKSPACE' ] = path . join ( __dirname , 'data' );
debugSpy = jest . spyOn ( core , 'debug' );
debugSpy . mockImplementation ( msg => {});
2022-03-31 21:10:37 +02:00
info = jest . spyOn ( core , 'info' );
warningSpy = jest . spyOn ( core , 'warning' );
isFeatureAvailable = jest . spyOn ( cache , 'isFeatureAvailable' );
2021-06-16 09:52:44 +03:00
getCommandOutputSpy = jest . spyOn ( utils , 'getCommandOutput' );
2023-07-13 14:06:23 +02:00
fsRealPathSyncSpy = jest . spyOn ( fs , 'realpathSync' );
fsRealPathSyncSpy . mockImplementation ( dirName => {
return dirName ;
});
});
afterEach (() => {
jest . resetAllMocks ();
jest . clearAllMocks ();
//jest.restoreAllMocks();
2021-06-16 09:52:44 +03:00
});
2023-07-13 14:06:23 +02:00
afterAll ( async () => {
console . log ( '::stoptoken::' );
jest . restoreAllMocks ();
}, 100000 );
2021-06-16 09:52:44 +03:00
describe ( 'getPackageManagerInfo' , () => {
2021-06-29 13:34:35 +03:00
it . each < [ string , PackageManagerInfo | null ] > ([
2021-06-16 09:52:44 +03:00
[ 'npm' , utils . supportedPackageManagers . npm ],
2021-06-30 16:44:51 +01:00
[ 'pnpm' , utils . supportedPackageManagers . pnpm ],
2023-06-21 17:52:17 +02:00
[ 'yarn' , utils . supportedPackageManagers . yarn ],
2021-06-16 09:52:44 +03:00
[ 'yarn1' , null ],
[ 'yarn2' , null ],
[ 'npm7' , null ]
])( 'getPackageManagerInfo for %s is %o' , async ( packageManager , result ) => {
getCommandOutputSpy . mockImplementationOnce (() => versionYarn1 );
await expect ( utils . getPackageManagerInfo ( packageManager )). resolves . toBe (
result
);
});
});
2022-03-31 21:10:37 +02:00
it ( 'isCacheFeatureAvailable for GHES is false' , () => {
isFeatureAvailable . mockImplementation (() => false );
process . env [ 'GITHUB_SERVER_URL' ] = 'https://www.test.com' ;
2022-12-09 12:05:59 +01:00
expect ( isCacheFeatureAvailable ()). toBeFalsy ();
expect ( warningSpy ). toHaveBeenCalledWith (
2022-03-31 21:10:37 +02:00
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
);
});
it ( 'isCacheFeatureAvailable for GHES has an interhal error' , () => {
isFeatureAvailable . mockImplementation (() => false );
process . env [ 'GITHUB_SERVER_URL' ] = '' ;
isCacheFeatureAvailable ();
expect ( warningSpy ). toHaveBeenCalledWith (
'The runner was not able to contact the cache service. Caching will be skipped'
);
});
it ( 'isCacheFeatureAvailable for GHES is available' , () => {
isFeatureAvailable . mockImplementation (() => true );
expect ( isCacheFeatureAvailable ()). toStrictEqual ( true );
});
2021-06-16 09:52:44 +03:00
afterEach (() => {
2022-03-31 21:10:37 +02:00
process . env [ 'GITHUB_SERVER_URL' ] = '' ;
2021-06-16 09:52:44 +03:00
jest . resetAllMocks ();
jest . clearAllMocks ();
});
2023-06-21 17:52:17 +02:00
describe ( 'getCacheDirectoriesPaths' , () => {
let existsSpy : jest.SpyInstance ;
let lstatSpy : jest.SpyInstance ;
let globCreateSpy : jest.SpyInstance ;
beforeEach (() => {
existsSpy = jest . spyOn ( fs , 'existsSync' );
existsSpy . mockImplementation (() => true );
lstatSpy = jest . spyOn ( fs , 'lstatSync' );
lstatSpy . mockImplementation ( arg => ({
isDirectory : () => true
}));
globCreateSpy = jest . spyOn ( glob , 'create' );
globCreateSpy . mockImplementation (
( pattern : string ) : Promise < Globber > =>
MockGlobber . create ([ '/foo' , '/bar' ])
);
2023-06-27 13:07:43 +02:00
resetProjectDirectoriesMemoized ();
2023-06-21 17:52:17 +02:00
});
afterEach (() => {
existsSpy . mockRestore ();
lstatSpy . mockRestore ();
globCreateSpy . mockRestore ();
});
it . each ([
[ supportedPackageManagers . npm , '' ],
[ supportedPackageManagers . npm , '/dir/file.lock' ],
[ supportedPackageManagers . npm , '/**/file.lock' ],
[ supportedPackageManagers . pnpm , '' ],
[ supportedPackageManagers . pnpm , '/dir/file.lock' ],
[ supportedPackageManagers . pnpm , '/**/file.lock' ]
])(
'getCacheDirectoriesPaths should return one dir for non yarn' ,
async ( packageManagerInfo , cacheDependency ) => {
getCommandOutputSpy . mockImplementation (() => '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 );
}
);
it ( 'getCacheDirectoriesPaths should return one dir for yarn without cacheDependency' , async () => {
getCommandOutputSpy . mockImplementation (() => 'foo' );
const dirs = await cacheUtils . getCacheDirectories (
supportedPackageManagers . yarn ,
''
);
expect ( dirs ). toEqual ([ 'foo' ]);
});
it . each ([
[ supportedPackageManagers . npm , '' ],
[ supportedPackageManagers . npm , '/dir/file.lock' ],
[ supportedPackageManagers . npm , '/**/file.lock' ],
[ supportedPackageManagers . pnpm , '' ],
[ supportedPackageManagers . pnpm , '/dir/file.lock' ],
[ supportedPackageManagers . pnpm , '/**/file.lock' ],
[ supportedPackageManagers . yarn , '' ],
[ supportedPackageManagers . yarn , '/dir/file.lock' ],
[ supportedPackageManagers . yarn , '/**/file.lock' ]
])(
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty' ,
async ( packageManagerInfo , cacheDependency ) => {
getCommandOutputSpy . mockImplementation (( command : string ) =>
// return empty string to indicate getCacheFolderPath failed
// --version still works
command . includes ( 'version' ) ? '1.' : ''
);
await expect (
cacheUtils . getCacheDirectories ( packageManagerInfo , cacheDependency )
). rejects . toThrow (); //'Could not get cache folder path for /dir');
}
);
it . each ([
[ supportedPackageManagers . yarn , '/dir/file.lock' ],
[ supportedPackageManagers . yarn , '/**/file.lock' ]
])(
'getCacheDirectoriesPaths should nothrow in case of having not directories' ,
async ( packageManagerInfo , cacheDependency ) => {
lstatSpy . mockImplementation ( arg => ({
isDirectory : () => false
}));
await cacheUtils . getCacheDirectories (
packageManagerInfo ,
cacheDependency
);
expect ( warningSpy ). toHaveBeenCalledTimes ( 1 );
expect ( warningSpy ). toHaveBeenCalledWith (
`No existing directories found containing cache-dependency-path=" ${ cacheDependency } "`
);
}
);
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 } ` );
const dirs = await cacheUtils . getCacheDirectories (
supportedPackageManagers . yarn ,
''
);
expect ( dirs ). toEqual ([ `foo ${ version } ` ]);
}
);
it . each ([ '1.1.1' , '2.2.2' ])(
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency' ,
async version => {
let dirNo = 1 ;
getCommandOutputSpy . mockImplementation (( command : string ) =>
command . includes ( 'version' ) ? version : `file_ ${ version } _ ${ dirNo ++ } `
);
globCreateSpy . mockImplementation (
( pattern : string ) : Promise < Globber > =>
MockGlobber . create ([ '/tmp/dir1/file' , '/tmp/dir2/file' ])
);
const dirs = await cacheUtils . getCacheDirectories (
supportedPackageManagers . yarn ,
'/tmp/**/file'
);
expect ( dirs ). toEqual ([ `file_ ${ version } _1` , `file_ ${ version } _2` ]);
}
);
it . each ([ '1.1.1' , '2.2.2' ])(
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates' ,
async version => {
let dirNo = 1 ;
getCommandOutputSpy . mockImplementation (( 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'
])
);
const dirs = await cacheUtils . getCacheDirectories (
supportedPackageManagers . yarn ,
'/tmp/**/file'
);
expect ( dirs ). toEqual ([ `file_ ${ version } _1` , `file_ ${ version } _2` ]);
}
);
it . each ([ '1.1.1' , '2.2.2' ])(
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories' ,
async version => {
let dirNo = 1 ;
getCommandOutputSpy . mockImplementation (( 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'
])
);
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 (
'yarn --version' ,
'/tmp/dir1'
);
expect ( getCommandOutputSpy ). toHaveBeenCalledWith (
'yarn --version' ,
'/tmp/dir2'
);
expect ( getCommandOutputSpy ). toHaveBeenCalledWith (
'yarn --version' ,
'/tmp/dir3'
);
expect ( getCommandOutputSpy ). toHaveBeenCalledWith (
version . startsWith ( '1.' )
? 'yarn cache dir'
: 'yarn config get cacheFolder' ,
'/tmp/dir1'
);
expect ( getCommandOutputSpy ). toHaveBeenCalledWith (
version . startsWith ( '1.' )
? 'yarn cache dir'
: 'yarn config get cacheFolder' ,
'/tmp/dir2'
);
expect ( getCommandOutputSpy ). toHaveBeenCalledWith (
version . startsWith ( '1.' )
? 'yarn cache dir'
: 'yarn config get cacheFolder' ,
'/tmp/dir3'
);
}
);
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'
])
);
let dirNo = 1 ;
getCommandOutputSpy . mockImplementation (( command : string ) =>
command . includes ( 'version' ) ? version : `file_ ${ version } _ ${ dirNo ++ } `
);
const dirs = await cacheUtils . getCacheDirectories (
supportedPackageManagers . yarn ,
cacheDependencyPath
);
expect ( dirs ). toEqual ([
`file_ ${ version } _1` ,
`file_ ${ version } _2` ,
`file_ ${ version } _3` ,
`file_ ${ version } _4`
]);
}
);
});
2021-06-16 09:52:44 +03:00
});