UNPKG

@rushstack/rush-http-build-cache-plugin

Version:

Rush plugin for generic HTTP cloud build cache

129 lines (112 loc) 4.46 kB
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. jest.mock('@rushstack/rush-sdk/lib/utilities/WebClient', () => { return jest.requireActual('@microsoft/rush-lib/lib/utilities/WebClient'); }); import { type RushSession, EnvironmentConfiguration } from '@rushstack/rush-sdk'; import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; import { WebClient } from '@rushstack/rush-sdk/lib/utilities/WebClient'; import { HttpBuildCacheProvider, type IHttpBuildCacheProviderOptions } from '../HttpBuildCacheProvider'; const EXAMPLE_OPTIONS: IHttpBuildCacheProviderOptions = { url: 'https://buildcache.example.acme.com', tokenHandler: { exec: 'node', args: ['tokenHandler.js'] }, uploadMethod: 'POST', isCacheWriteAllowed: false, pluginName: 'example-plugin', rushJsonFolder: '/repo', minHttpRetryDelayMs: 1 }; type FetchFnType = Parameters<typeof WebClient.mockRequestFn>[0]; describe('HttpBuildCacheProvider', () => { let terminalBuffer: StringBufferTerminalProvider; let terminal!: Terminal; let fetchFn: jest.Mock; beforeEach(() => { terminalBuffer = new StringBufferTerminalProvider(); terminal = new Terminal(terminalBuffer); fetchFn = jest.fn(); WebClient.mockRequestFn(fetchFn as unknown as FetchFnType); }); afterEach(() => { WebClient.resetMockRequestFn(); }); describe('tryGetCacheEntryBufferByIdAsync', () => { it('prints warning if read credentials are not available', async () => { jest.spyOn(EnvironmentConfiguration, 'buildCacheCredential', 'get').mockReturnValue(undefined); const session: RushSession = {} as RushSession; const provider = new HttpBuildCacheProvider(EXAMPLE_OPTIONS, session); mocked(fetchFn).mockResolvedValue({ status: 401, statusText: 'Unauthorized', ok: false }); const result = await provider.tryGetCacheEntryBufferByIdAsync(terminal, 'some-key'); expect(result).toBe(undefined); expect(fetchFn).toHaveBeenCalledTimes(1); expect(fetchFn).toHaveBeenNthCalledWith( 1, 'https://buildcache.example.acme.com/some-key', expect.objectContaining({ method: 'GET', redirect: 'follow' }) ); expect(terminalBuffer.getWarningOutput()).toMatchInlineSnapshot( `"Error getting cache entry: Error: Credentials for https://buildcache.example.acme.com/ have not been provided.[n]In CI, verify that RUSH_BUILD_CACHE_CREDENTIAL contains a valid Authorization header value.[n][n]For local developers, run:[n][n] rush update-cloud-credentials --interactive[n][n]"` ); }); it('attempts up to 3 times to download a cache entry', async () => { jest.spyOn(EnvironmentConfiguration, 'buildCacheCredential', 'get').mockReturnValue(undefined); const session: RushSession = {} as RushSession; const provider = new HttpBuildCacheProvider(EXAMPLE_OPTIONS, session); mocked(fetchFn).mockResolvedValueOnce({ status: 500, statusText: 'InternalServiceError', ok: false }); mocked(fetchFn).mockResolvedValueOnce({ status: 503, statusText: 'ServiceUnavailable', ok: false }); mocked(fetchFn).mockResolvedValueOnce({ status: 504, statusText: 'BadGateway', ok: false }); const result = await provider.tryGetCacheEntryBufferByIdAsync(terminal, 'some-key'); expect(result).toBe(undefined); expect(fetchFn).toHaveBeenCalledTimes(3); expect(fetchFn).toHaveBeenNthCalledWith( 1, 'https://buildcache.example.acme.com/some-key', expect.objectContaining({ method: 'GET', redirect: 'follow' }) ); expect(fetchFn).toHaveBeenNthCalledWith( 2, 'https://buildcache.example.acme.com/some-key', expect.objectContaining({ method: 'GET', redirect: 'follow' }) ); expect(fetchFn).toHaveBeenNthCalledWith( 3, 'https://buildcache.example.acme.com/some-key', expect.objectContaining({ method: 'GET', redirect: 'follow' }) ); expect(terminalBuffer.getWarningOutput()).toMatchInlineSnapshot( `"Could not get cache entry: HTTP 504: BadGateway[n]"` ); }); }); });