2020-05-15 12:18:50 -04:00
import * as core from '@actions/core'
2020-05-06 11:10:18 -04:00
import * as path from 'path'
import { saveCache } from '../src/cache'
import * as cacheHttpClient from '../src/internal/cacheHttpClient'
import * as cacheUtils from '../src/internal/cacheUtils'
import { CacheFilename , CompressionMethod } from '../src/internal/constants'
import * as tar from '../src/internal/tar'
2022-05-11 17:14:25 -04:00
import { TypedResponse } from '@actions/http-client/lib/interfaces'
2022-04-04 16:21:58 +05:30
import {
ReserveCacheResponse ,
ITypedResponseWithError
} from '../src/internal/contracts'
import { HttpClientError } from '@actions/http-client'
2020-05-06 11:10:18 -04:00
jest . mock ( '../src/internal/cacheHttpClient' )
jest . mock ( '../src/internal/cacheUtils' )
jest . mock ( '../src/internal/tar' )
beforeAll (() => {
2020-05-15 12:18:50 -04:00
jest . spyOn ( console , 'log' ). mockImplementation (() => {})
jest . spyOn ( core , 'debug' ). mockImplementation (() => {})
jest . spyOn ( core , 'info' ). mockImplementation (() => {})
jest . spyOn ( core , 'warning' ). mockImplementation (() => {})
jest . spyOn ( core , 'error' ). mockImplementation (() => {})
2020-05-06 11:10:18 -04:00
jest . spyOn ( cacheUtils , 'getCacheFileName' ). mockImplementation ( cm => {
const actualUtils = jest . requireActual ( '../src/internal/cacheUtils' )
return actualUtils . getCacheFileName ( cm )
})
jest . spyOn ( cacheUtils , 'resolvePaths' ). mockImplementation ( async filePaths => {
return filePaths . map ( x => path . resolve ( x ))
})
jest . spyOn ( cacheUtils , 'createTempDirectory' ). mockImplementation ( async () => {
return Promise . resolve ( '/foo/bar' )
})
})
2020-05-06 17:53:22 -04:00
test ( 'save with missing input should fail' , async () => {
const paths : string [] = []
2020-05-06 11:10:18 -04:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2020-05-06 17:53:22 -04:00
await expect ( saveCache ( paths , primaryKey )). rejects . toThrowError (
`Path Validation Error: At least one directory or file path is required`
2020-05-06 11:10:18 -04:00
)
})
2020-05-06 17:53:22 -04:00
test ( 'save with large cache outputs should fail' , async () => {
const filePath = 'node_modules'
2020-05-06 11:10:18 -04:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2020-05-06 17:53:22 -04:00
const cachePaths = [ path . resolve ( filePath )]
2020-05-06 11:10:18 -04:00
const createTarMock = jest . spyOn ( tar , 'createTar' )
2022-06-20 04:22:20 +00:00
const logWarningMock = jest . spyOn ( core , "warning" );
2020-05-06 11:10:18 -04:00
2021-11-19 16:34:33 +05:30
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
2020-05-15 12:18:50 -04:00
jest
2021-05-03 18:09:44 +03:00
. spyOn ( cacheUtils , 'getArchiveFileSizeInBytes' )
2020-05-15 12:18:50 -04:00
. mockReturnValueOnce ( cacheSize )
2020-05-06 11:10:18 -04:00
const compression = CompressionMethod . Gzip
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
2020-05-06 17:53:22 -04:00
. mockReturnValueOnce ( Promise . resolve ( compression ))
2022-06-20 04:22:20 +00:00
const cacheId = await saveCache ([ filePath ], primaryKey )
expect ( cacheId ). toBe ( - 1 )
expect ( logWarningMock ). toHaveBeenCalledTimes ( 1 )
expect ( logWarningMock ). toHaveBeenCalledWith ( 'Fail to save: Error: Cache size of ~11264 MB (11811160064 B) is over the 10GB limit, not saving cache.' )
2020-05-06 11:10:18 -04:00
const archiveFolder = '/foo/bar'
2022-04-01 02:10:08 +05:30
2020-05-06 11:10:18 -04:00
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledWith (
archiveFolder ,
cachePaths ,
compression
)
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
2022-04-04 16:21:58 +05:30
test ( 'save with large cache outputs should fail in GHES with error message' , async () => {
const filePath = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const cachePaths = [ path . resolve ( filePath )]
const createTarMock = jest . spyOn ( tar , 'createTar' )
2022-06-20 04:22:20 +00:00
const logWarningMock = jest . spyOn ( core , "warning" );
2022-04-04 16:21:58 +05:30
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
. spyOn ( cacheUtils , 'getArchiveFileSizeInBytes' )
. mockReturnValueOnce ( cacheSize )
const compression = CompressionMethod . Gzip
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
. mockReturnValueOnce ( Promise . resolve ( compression ))
jest . spyOn ( cacheUtils , 'isGhes' ). mockReturnValueOnce ( true )
const reserveCacheMock = jest
. spyOn ( cacheHttpClient , 'reserveCache' )
. mockImplementation ( async () => {
const response : ITypedResponseWithError < ReserveCacheResponse > = {
statusCode : 400 ,
result : null ,
headers : {},
error : new HttpClientError (
'The cache filesize must be between 0 and 1073741824 bytes' ,
400
)
}
return response
})
2022-06-20 04:22:20 +00:00
const cacheId = await saveCache ([ filePath ], primaryKey )
expect ( cacheId ). toBe ( - 1 )
expect ( logWarningMock ). toHaveBeenCalledTimes ( 1 )
expect ( logWarningMock ). toHaveBeenCalledWith ( 'Fail to save: Error: The cache filesize must be between 0 and 1073741824 bytes' )
2022-04-04 16:21:58 +05:30
const archiveFolder = '/foo/bar'
expect ( reserveCacheMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledWith (
archiveFolder ,
cachePaths ,
compression
)
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
test ( 'save with large cache outputs should fail in GHES without error message' , async () => {
const filePath = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const cachePaths = [ path . resolve ( filePath )]
const createTarMock = jest . spyOn ( tar , 'createTar' )
2022-06-20 04:22:20 +00:00
const logWarningMock = jest . spyOn ( core , "warning" );
2022-04-04 16:21:58 +05:30
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
. spyOn ( cacheUtils , 'getArchiveFileSizeInBytes' )
. mockReturnValueOnce ( cacheSize )
const compression = CompressionMethod . Gzip
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
. mockReturnValueOnce ( Promise . resolve ( compression ))
jest . spyOn ( cacheUtils , 'isGhes' ). mockReturnValueOnce ( true )
const reserveCacheMock = jest
. spyOn ( cacheHttpClient , 'reserveCache' )
. mockImplementation ( async () => {
const response : ITypedResponseWithError < ReserveCacheResponse > = {
statusCode : 400 ,
result : null ,
headers : {}
}
return response
})
2022-06-20 04:22:20 +00:00
const cacheId = await saveCache ([ filePath ], primaryKey )
expect ( cacheId ). toBe ( - 1 )
expect ( logWarningMock ). toHaveBeenCalledTimes ( 1 )
expect ( logWarningMock ). toHaveBeenCalledWith ( 'Fail to save: Error: Cache size of ~11264 MB (11811160064 B) is over the data cap limit, not saving cache.' )
2022-04-04 16:21:58 +05:30
const archiveFolder = '/foo/bar'
expect ( reserveCacheMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledWith (
archiveFolder ,
cachePaths ,
compression
)
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
2020-05-06 17:53:22 -04:00
test ( 'save with reserve cache failure should fail' , async () => {
const paths = [ 'node_modules' ]
2020-05-06 11:10:18 -04:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2022-06-20 04:22:20 +00:00
const logWarningMock = jest . spyOn ( core , "warning" );
2020-05-06 11:10:18 -04:00
const reserveCacheMock = jest
. spyOn ( cacheHttpClient , 'reserveCache' )
. mockImplementation ( async () => {
2022-05-11 17:14:25 -04:00
const response : TypedResponse < ReserveCacheResponse > = {
2022-04-04 16:21:58 +05:30
statusCode : 500 ,
result : null ,
headers : {}
}
return response
2020-05-06 11:10:18 -04:00
})
const createTarMock = jest . spyOn ( tar , 'createTar' )
const saveCacheMock = jest . spyOn ( cacheHttpClient , 'saveCache' )
const compression = CompressionMethod . Zstd
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
2020-05-06 17:53:22 -04:00
. mockReturnValueOnce ( Promise . resolve ( compression ))
2020-05-06 11:10:18 -04:00
2022-06-20 04:22:20 +00:00
const cacheId = await saveCache ( paths , primaryKey )
expect ( cacheId ). toBe ( - 1 )
expect ( logWarningMock ). toHaveBeenCalledTimes ( 1 )
expect ( logWarningMock ). toHaveBeenCalledWith ( 'Fail to save: ReserveCacheError: Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.' )
2020-05-06 11:10:18 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledTimes ( 1 )
2020-05-06 17:53:22 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledWith ( primaryKey , paths , {
2020-05-06 11:10:18 -04:00
compressionMethod : compression
})
2022-04-04 16:21:58 +05:30
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
2020-05-06 11:10:18 -04:00
expect ( saveCacheMock ). toHaveBeenCalledTimes ( 0 )
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
2020-05-06 17:53:22 -04:00
test ( 'save with server error should fail' , async () => {
const filePath = 'node_modules'
2020-05-06 11:10:18 -04:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2020-05-06 17:53:22 -04:00
const cachePaths = [ path . resolve ( filePath )]
2022-06-20 04:22:20 +00:00
const logWarningMock = jest . spyOn ( core , "warning" );
2020-05-06 11:10:18 -04:00
const cacheId = 4
const reserveCacheMock = jest
. spyOn ( cacheHttpClient , 'reserveCache' )
. mockImplementation ( async () => {
2022-05-11 17:14:25 -04:00
const response : TypedResponse < ReserveCacheResponse > = {
2022-04-04 16:21:58 +05:30
statusCode : 500 ,
result : { cacheId },
headers : {}
}
return response
2020-05-06 11:10:18 -04:00
})
const createTarMock = jest . spyOn ( tar , 'createTar' )
const saveCacheMock = jest
. spyOn ( cacheHttpClient , 'saveCache' )
2020-05-06 20:07:39 -04:00
. mockImplementationOnce (() => {
2020-05-06 11:10:18 -04:00
throw new Error ( 'HTTP Error Occurred' )
})
const compression = CompressionMethod . Zstd
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
2020-05-06 17:53:22 -04:00
. mockReturnValueOnce ( Promise . resolve ( compression ))
2022-06-20 04:22:20 +00:00
await saveCache ([ filePath ], primaryKey )
expect ( logWarningMock ). toHaveBeenCalledTimes ( 1 )
expect ( logWarningMock ). toHaveBeenCalledWith ( 'Fail to save: Error: HTTP Error Occurred' )
2020-05-06 11:10:18 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledTimes ( 1 )
2020-05-06 17:53:22 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledWith ( primaryKey , [ filePath ], {
2020-05-06 11:10:18 -04:00
compressionMethod : compression
})
const archiveFolder = '/foo/bar'
const archiveFile = path . join ( archiveFolder , CacheFilename . Zstd )
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledWith (
archiveFolder ,
cachePaths ,
compression
)
expect ( saveCacheMock ). toHaveBeenCalledTimes ( 1 )
2020-05-12 12:37:03 -04:00
expect ( saveCacheMock ). toHaveBeenCalledWith ( cacheId , archiveFile , undefined )
2020-05-06 11:10:18 -04:00
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
test ( 'save with valid inputs uploads a cache' , async () => {
2020-05-06 17:53:22 -04:00
const filePath = 'node_modules'
2020-05-06 11:10:18 -04:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2020-05-06 17:53:22 -04:00
const cachePaths = [ path . resolve ( filePath )]
2020-05-06 11:10:18 -04:00
const cacheId = 4
const reserveCacheMock = jest
. spyOn ( cacheHttpClient , 'reserveCache' )
. mockImplementation ( async () => {
2022-05-11 17:14:25 -04:00
const response : TypedResponse < ReserveCacheResponse > = {
2022-04-04 16:21:58 +05:30
statusCode : 500 ,
result : { cacheId },
headers : {}
}
return response
2020-05-06 11:10:18 -04:00
})
const createTarMock = jest . spyOn ( tar , 'createTar' )
const saveCacheMock = jest . spyOn ( cacheHttpClient , 'saveCache' )
const compression = CompressionMethod . Zstd
const getCompressionMock = jest
. spyOn ( cacheUtils , 'getCompressionMethod' )
. mockReturnValue ( Promise . resolve ( compression ))
2020-05-06 17:53:22 -04:00
await saveCache ([ filePath ], primaryKey )
2020-05-06 11:10:18 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledTimes ( 1 )
2020-05-06 17:53:22 -04:00
expect ( reserveCacheMock ). toHaveBeenCalledWith ( primaryKey , [ filePath ], {
2020-05-06 11:10:18 -04:00
compressionMethod : compression
})
const archiveFolder = '/foo/bar'
const archiveFile = path . join ( archiveFolder , CacheFilename . Zstd )
expect ( createTarMock ). toHaveBeenCalledTimes ( 1 )
expect ( createTarMock ). toHaveBeenCalledWith (
archiveFolder ,
cachePaths ,
compression
)
expect ( saveCacheMock ). toHaveBeenCalledTimes ( 1 )
2020-05-12 12:37:03 -04:00
expect ( saveCacheMock ). toHaveBeenCalledWith ( cacheId , archiveFile , undefined )
2020-05-06 11:10:18 -04:00
expect ( getCompressionMock ). toHaveBeenCalledTimes ( 1 )
})
2022-05-23 06:32:13 +00:00
test ( 'save with non existing path should not save cache' , async () => {
2022-05-23 06:49:26 +00:00
const path = 'node_modules'
2022-05-23 06:32:13 +00:00
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
2022-05-23 06:49:26 +00:00
jest . spyOn ( cacheUtils , 'resolvePaths' ). mockImplementation ( async () => {
2022-05-23 06:32:13 +00:00
return []
})
2022-05-23 06:49:26 +00:00
await expect ( saveCache ([ path ], primaryKey )). rejects . toThrowError (
2022-05-23 12:05:31 +00:00
`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`
2022-05-23 06:32:13 +00:00
)
})