2022-03-30 18:16:49 +08:00
import * as cache from "@actions/cache" ;
2019-11-14 05:13:00 +08:00
import * as core from "@actions/core" ;
2023-04-08 09:38:17 +08:00
import { RequestError } from "@octokit/request-error" ;
import nock from "nock" ;
2019-11-14 05:13:00 +08:00
2022-12-21 22:08:44 +08:00
import { Events , RefKey } from "../src/constants" ;
2019-11-14 05:13:00 +08:00
import * as actionUtils from "../src/utils/actionUtils" ;
2020-06-02 23:21:03 +08:00
import * as testUtils from "../src/utils/testUtils" ;
2019-11-14 05:13:00 +08:00
jest . mock ( "@actions/core" ) ;
2022-03-30 18:16:49 +08:00
jest . mock ( "@actions/cache" ) ;
2020-03-21 04:02:11 +08:00
2020-06-02 23:21:03 +08:00
beforeAll ( ( ) = > {
2023-04-08 09:38:17 +08:00
nock . disableNetConnect ( ) ;
2020-06-02 23:21:03 +08:00
jest . spyOn ( core , "getInput" ) . mockImplementation ( ( name , options ) = > {
return jest . requireActual ( "@actions/core" ) . getInput ( name , options ) ;
} ) ;
2023-04-08 09:38:17 +08:00
testUtils . mockServer . listen ( {
onUnhandledRequest : "warn"
} ) ;
2020-06-02 23:21:03 +08:00
} ) ;
2019-11-14 05:13:00 +08:00
afterEach ( ( ) = > {
delete process . env [ Events . Key ] ;
2020-04-18 03:46:46 +08:00
delete process . env [ RefKey ] ;
2023-04-08 09:38:17 +08:00
delete process . env [ "GITHUB_REPOSITORY" ] ;
delete process . env [ "GITHUB_TOKEN" ] ;
delete process . env [ "GITHUB_ACTION" ] ;
} ) ;
afterAll ( ( ) = > {
testUtils . mockServer . close ( ) ;
nock . enableNetConnect ( ) ;
2019-11-14 05:13:00 +08:00
} ) ;
2020-09-30 01:36:19 +08:00
test ( "isGhes returns true if server url is not github.com" , ( ) = > {
try {
process . env [ "GITHUB_SERVER_URL" ] = "http://example.com" ;
expect ( actionUtils . isGhes ( ) ) . toBe ( true ) ;
} finally {
process . env [ "GITHUB_SERVER_URL" ] = undefined ;
}
} ) ;
2022-02-07 13:20:40 +08:00
test ( "isGhes returns false when server url is github.com" , ( ) = > {
2020-09-30 01:36:19 +08:00
try {
process . env [ "GITHUB_SERVER_URL" ] = "http://github.com" ;
expect ( actionUtils . isGhes ( ) ) . toBe ( false ) ;
} finally {
process . env [ "GITHUB_SERVER_URL" ] = undefined ;
}
} ) ;
2020-05-15 05:27:38 +08:00
test ( "isExactKeyMatch with undefined cache key returns false" , ( ) = > {
2019-11-14 05:13:00 +08:00
const key = "linux-rust" ;
2020-05-15 05:27:38 +08:00
const cacheKey = undefined ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( false ) ;
2019-11-14 05:13:00 +08:00
} ) ;
2020-05-15 05:27:38 +08:00
test ( "isExactKeyMatch with empty cache key returns false" , ( ) = > {
2019-11-14 05:13:00 +08:00
const key = "linux-rust" ;
2020-05-15 05:27:38 +08:00
const cacheKey = "" ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( false ) ;
2019-11-14 05:13:00 +08:00
} ) ;
test ( "isExactKeyMatch with different keys returns false" , ( ) = > {
const key = "linux-rust" ;
2020-05-15 05:27:38 +08:00
const cacheKey = "linux-" ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( false ) ;
2019-11-14 05:13:00 +08:00
} ) ;
test ( "isExactKeyMatch with different key accents returns false" , ( ) = > {
const key = "linux-áccent" ;
2020-05-15 05:27:38 +08:00
const cacheKey = "linux-accent" ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( false ) ;
2019-11-14 05:13:00 +08:00
} ) ;
test ( "isExactKeyMatch with same key returns true" , ( ) = > {
const key = "linux-rust" ;
2020-05-15 05:27:38 +08:00
const cacheKey = "linux-rust" ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( true ) ;
2019-11-14 05:13:00 +08:00
} ) ;
test ( "isExactKeyMatch with same key and different casing returns true" , ( ) = > {
const key = "linux-rust" ;
2020-05-15 05:27:38 +08:00
const cacheKey = "LINUX-RUST" ;
2019-11-14 05:13:00 +08:00
2020-05-15 05:27:38 +08:00
expect ( actionUtils . isExactKeyMatch ( key , cacheKey ) ) . toBe ( true ) ;
2019-11-14 05:13:00 +08:00
} ) ;
2019-11-22 03:37:54 +08:00
test ( "logWarning logs a message with a warning prefix" , ( ) = > {
const message = "A warning occurred." ;
const infoMock = jest . spyOn ( core , "info" ) ;
actionUtils . logWarning ( message ) ;
expect ( infoMock ) . toHaveBeenCalledWith ( ` [warning] ${ message } ` ) ;
} ) ;
2020-04-18 03:46:46 +08:00
test ( "isValidEvent returns false for event that does not have a branch or tag" , ( ) = > {
2019-11-14 05:13:00 +08:00
const event = "foo" ;
process . env [ Events . Key ] = event ;
const isValidEvent = actionUtils . isValidEvent ( ) ;
expect ( isValidEvent ) . toBe ( false ) ;
} ) ;
2020-04-18 03:46:46 +08:00
test ( "isValidEvent returns true for event that has a ref" , ( ) = > {
2019-11-14 05:13:00 +08:00
const event = Events . Push ;
process . env [ Events . Key ] = event ;
2020-04-18 03:46:46 +08:00
process . env [ RefKey ] = "ref/heads/feature" ;
2019-11-14 05:13:00 +08:00
const isValidEvent = actionUtils . isValidEvent ( ) ;
expect ( isValidEvent ) . toBe ( true ) ;
} ) ;
2020-06-02 23:21:03 +08:00
test ( "getInputAsArray returns empty array if not required and missing" , ( ) = > {
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [ ] ) ;
} ) ;
test ( "getInputAsArray throws error if required and missing" , ( ) = > {
expect ( ( ) = >
actionUtils . getInputAsArray ( "foo" , { required : true } )
) . toThrowError ( ) ;
} ) ;
test ( "getInputAsArray handles single line correctly" , ( ) = > {
testUtils . setInput ( "foo" , "bar" ) ;
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [ "bar" ] ) ;
} ) ;
test ( "getInputAsArray handles multiple lines correctly" , ( ) = > {
testUtils . setInput ( "foo" , "bar\nbaz" ) ;
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [ "bar" , "baz" ] ) ;
} ) ;
test ( "getInputAsArray handles different new lines correctly" , ( ) = > {
testUtils . setInput ( "foo" , "bar\r\nbaz" ) ;
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [ "bar" , "baz" ] ) ;
} ) ;
test ( "getInputAsArray handles empty lines correctly" , ( ) = > {
testUtils . setInput ( "foo" , "\n\nbar\n\nbaz\n\n" ) ;
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [ "bar" , "baz" ] ) ;
} ) ;
2020-10-02 22:59:55 +08:00
2020-11-29 03:34:06 +08:00
test ( "getInputAsArray removes spaces after ! at the beginning" , ( ) = > {
testUtils . setInput (
"foo" ,
"! bar\n! baz\n! qux\n!quux\ncorge\ngrault! garply\n!\r\t waldo"
) ;
expect ( actionUtils . getInputAsArray ( "foo" ) ) . toEqual ( [
"!bar" ,
"!baz" ,
"!qux" ,
2022-10-03 14:39:10 +08:00
"!quux" ,
2020-11-29 03:34:06 +08:00
"corge" ,
2022-10-03 14:39:10 +08:00
"grault! garply" ,
"!waldo"
2020-11-29 03:34:06 +08:00
] ) ;
} ) ;
2020-10-02 22:59:55 +08:00
test ( "getInputAsInt returns undefined if input not set" , ( ) = > {
2020-10-02 23:55:30 +08:00
expect ( actionUtils . getInputAsInt ( "undefined" ) ) . toBeUndefined ( ) ;
2020-10-02 22:59:55 +08:00
} ) ;
test ( "getInputAsInt returns value if input is valid" , ( ) = > {
testUtils . setInput ( "foo" , "8" ) ;
expect ( actionUtils . getInputAsInt ( "foo" ) ) . toBe ( 8 ) ;
} ) ;
test ( "getInputAsInt returns undefined if input is invalid or NaN" , ( ) = > {
testUtils . setInput ( "foo" , "bar" ) ;
expect ( actionUtils . getInputAsInt ( "foo" ) ) . toBeUndefined ( ) ;
} ) ;
2020-10-02 23:55:30 +08:00
test ( "getInputAsInt throws if required and value missing" , ( ) = > {
expect ( ( ) = >
actionUtils . getInputAsInt ( "undefined" , { required : true } )
) . toThrowError ( ) ;
} ) ;
2022-03-30 18:16:49 +08:00
2023-01-05 19:19:13 +08:00
test ( "getInputAsBool returns false if input not set" , ( ) = > {
expect ( actionUtils . getInputAsBool ( "undefined" ) ) . toBe ( false ) ;
} ) ;
test ( "getInputAsBool returns value if input is valid" , ( ) = > {
testUtils . setInput ( "foo" , "true" ) ;
expect ( actionUtils . getInputAsBool ( "foo" ) ) . toBe ( true ) ;
} ) ;
test ( "getInputAsBool returns false if input is invalid or NaN" , ( ) = > {
testUtils . setInput ( "foo" , "bar" ) ;
expect ( actionUtils . getInputAsBool ( "foo" ) ) . toBe ( false ) ;
} ) ;
test ( "getInputAsBool throws if required and value missing" , ( ) = > {
expect ( ( ) = >
actionUtils . getInputAsBool ( "undefined2" , { required : true } )
) . toThrowError ( ) ;
} ) ;
2023-04-08 09:38:17 +08:00
test ( "deleteCacheByKey returns 'HttpError: 404' when cache is not found." , async ( ) = > {
const event = Events . Push ;
process . env [ "GITHUB_REPOSITORY" ] = "owner/repo" ;
process . env [ "GITHUB_TOKEN" ] =
"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" ;
process . env [ "GITHUB_ACTION" ] = "__owner___run-repo" ;
process . env [ Events . Key ] = event ;
process . env [ RefKey ] = "ref/heads/feature" ;
const logWarningMock = jest . spyOn ( actionUtils , "logWarning" ) ;
const response = await actionUtils . deleteCacheByKey (
testUtils . failureCacheKey ,
"owner" ,
"repo"
) ;
expect ( logWarningMock ) . toHaveBeenCalledWith (
expect . stringMatching ( /404: Not Found/i )
) ;
expect ( response ) . toBeInstanceOf ( RequestError ) ;
expect ( response ) . toMatchObject ( {
name : "HttpError" ,
status : 404
} ) ;
} ) ;
test ( "deleteCacheByKey returns 'HttpError: 401' on an invalid non-mocked request." , async ( ) = > {
const event = Events . Push ;
process . env [ "GITHUB_REPOSITORY" ] = "owner/repo" ;
process . env [ "GITHUB_TOKEN" ] =
"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" ;
process . env [ "GITHUB_ACTION" ] = "__owner___run-repo" ;
process . env [ Events . Key ] = event ;
process . env [ RefKey ] = "ref/heads/feature" ;
await nock . enableNetConnect ( ) ;
const logWarningMock = jest . spyOn ( actionUtils , "logWarning" ) ;
const response = await actionUtils . deleteCacheByKey (
testUtils . passThroughCacheKey ,
"owner" ,
"repo"
) ;
expect ( logWarningMock ) . toHaveBeenCalledWith (
expect . stringMatching ( /401: Bad Credentials/i )
) ;
expect ( response ) . toBeInstanceOf ( RequestError ) ;
expect ( response ) . toMatchObject ( {
name : "HttpError" ,
status : 401
} ) ;
nock . disableNetConnect ( ) ;
} ) ;
test ( "deleteCacheByKey returns matched cache data when successful." , async ( ) = > {
const event = Events . Push ;
process . env [ "GITHUB_REPOSITORY" ] = "owner/repo" ;
process . env [ "GITHUB_TOKEN" ] =
"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" ;
process . env [ "GITHUB_ACTION" ] = "__owner___run-repo" ;
process . env [ Events . Key ] = event ;
process . env [ RefKey ] = "ref/heads/feature" ;
const expectedResponse = {
id : expect.any ( Number ) ,
ref : expect.any ( String ) ,
key : expect.any ( String ) ,
version : expect.any ( String ) ,
last_accessed_at : expect.any ( String ) ,
created_at : expect.any ( String ) ,
size_in_bytes : expect.any ( Number )
} ;
const logWarningMock = jest . spyOn ( actionUtils , "logWarning" ) ;
const response = await actionUtils . deleteCacheByKey (
testUtils . successCacheKey ,
"owner" ,
"repo"
) ;
expect ( response ) . toMatchObject ( {
data : expect.objectContaining ( {
total_count : expect.any ( Number ) ,
actions_caches : expect.arrayContaining ( [
expect . objectContaining ( expectedResponse )
] )
} )
} ) ;
expect ( logWarningMock ) . toHaveBeenCalledTimes ( 0 ) ;
} ) ;
2022-03-30 18:16:49 +08:00
test ( "isCacheFeatureAvailable for ac enabled" , ( ) = > {
jest . spyOn ( cache , "isFeatureAvailable" ) . mockImplementation ( ( ) = > true ) ;
expect ( actionUtils . isCacheFeatureAvailable ( ) ) . toBe ( true ) ;
} ) ;
test ( "isCacheFeatureAvailable for ac disabled on GHES" , ( ) = > {
jest . spyOn ( cache , "isFeatureAvailable" ) . mockImplementation ( ( ) = > false ) ;
2022-09-20 13:17:27 +08:00
const message = ` Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.
Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect , please unretire the actions / cache namespace before upgrade ( see https : //docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`;
2022-03-30 18:16:49 +08:00
const infoMock = jest . spyOn ( core , "info" ) ;
try {
process . env [ "GITHUB_SERVER_URL" ] = "http://example.com" ;
expect ( actionUtils . isCacheFeatureAvailable ( ) ) . toBe ( false ) ;
expect ( infoMock ) . toHaveBeenCalledWith ( ` [warning] ${ message } ` ) ;
} finally {
delete process . env [ "GITHUB_SERVER_URL" ] ;
}
} ) ;
test ( "isCacheFeatureAvailable for ac disabled on dotcom" , ( ) = > {
jest . spyOn ( cache , "isFeatureAvailable" ) . mockImplementation ( ( ) = > false ) ;
const message =
"An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions." ;
const infoMock = jest . spyOn ( core , "info" ) ;
try {
process . env [ "GITHUB_SERVER_URL" ] = "http://github.com" ;
expect ( actionUtils . isCacheFeatureAvailable ( ) ) . toBe ( false ) ;
expect ( infoMock ) . toHaveBeenCalledWith ( ` [warning] ${ message } ` ) ;
} finally {
delete process . env [ "GITHUB_SERVER_URL" ] ;
}
} ) ;