import MockAdapter from 'axios-mock-adapter'; import { HTTP_STATUS_OK, HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status'; import axios from '~/lib/utils/axios_utils'; import pageInfoQuery from '~/graphql_shared/client/page_info.query.graphql'; import currentPageQuery from '~/deploy_keys/graphql/queries/current_page.query.graphql'; import currentScopeQuery from '~/deploy_keys/graphql/queries/current_scope.query.graphql'; import confirmRemoveKeyQuery from '~/deploy_keys/graphql/queries/confirm_remove_key.query.graphql'; import { resolvers } from '~/deploy_keys/graphql/resolvers'; const ENDPOINTS = { enabledKeysEndpoint: '/enabled_keys', availableProjectKeysEndpoint: '/available_project_keys', availablePublicKeysEndpoint: '/available_public_keys', }; describe('~/deploy_keys/graphql/resolvers', () => { let mockResolvers; let mock; let client; beforeEach(() => { mockResolvers = resolvers(ENDPOINTS); mock = new MockAdapter(axios); client = { writeQuery: jest.fn(), readQuery: jest.fn(), readFragment: jest.fn(), cache: { evict: jest.fn(), gc: jest.fn() }, }; }); afterEach(() => { mock.reset(); }); describe('deployKeys', () => { const key = { id: 1, title: 'hello', edit_path: '/edit' }; it.each(['enabledKeys', 'availableProjectKeys', 'availablePublicKeys'])( 'should request the endpoint for the %s scope', async (scope) => { mock.onGet(ENDPOINTS[`${scope}Endpoint`]).reply(HTTP_STATUS_OK, { keys: [key] }); const keys = await mockResolvers.Project.deployKeys(null, { scope, page: 1 }, { client }); expect(keys).toEqual([ { id: 1, title: 'hello', editPath: '/edit', __typename: 'LocalDeployKey' }, ]); }, ); it('should default to enabled keys if a bad scope is given', async () => { const scope = 'bad'; mock.onGet(ENDPOINTS.enabledKeysEndpoint).reply(HTTP_STATUS_OK, { keys: [key] }); const keys = await mockResolvers.Project.deployKeys(null, { scope, page: 1 }, { client }); expect(keys).toEqual([ { id: 1, title: 'hello', editPath: '/edit', __typename: 'LocalDeployKey' }, ]); }); it('should request the given page', async () => { const scope = 'enabledKeys'; const page = 2; mock .onGet(ENDPOINTS.enabledKeysEndpoint, { params: { page } }) .reply(HTTP_STATUS_OK, { keys: [key] }); const keys = await mockResolvers.Project.deployKeys(null, { scope, page }, { client }); expect(keys).toEqual([ { id: 1, title: 'hello', editPath: '/edit', __typename: 'LocalDeployKey' }, ]); }); it('should write pagination info to the cache', async () => { const scope = 'enabledKeys'; const page = 1; mock.onGet(ENDPOINTS.enabledKeysEndpoint).reply( HTTP_STATUS_OK, { keys: [key] }, { 'x-next-page': '2', 'x-page': '1', 'X-Per-Page': '2', 'X-Prev-Page': '', 'X-TOTAL': '37', 'X-Total-Pages': '5', }, ); await mockResolvers.Project.deployKeys(null, { scope, page }, { client }); expect(client.writeQuery).toHaveBeenCalledWith({ query: pageInfoQuery, variables: { input: { scope, page } }, data: { pageInfo: { total: 37, perPage: 2, previousPage: NaN, totalPages: 5, nextPage: 2, page: 1, __typename: 'LocalPageInfo', }, }, }); }); it('should not write page info if the request fails', async () => { const scope = 'enabledKeys'; const page = 1; mock.onGet(ENDPOINTS.enabledKeysEndpoint).reply(HTTP_STATUS_NOT_FOUND); try { await mockResolvers.Project.deployKeys(null, { scope, page }, { client }); } catch { expect(client.writeQuery).not.toHaveBeenCalled(); } }); }); describe('currentPage', () => { it('sets the current page', () => { const page = 5; mockResolvers.Mutation.currentPage(null, { page }, { client }); expect(client.writeQuery).toHaveBeenCalledWith({ query: currentPageQuery, data: { currentPage: page }, }); }); }); describe('currentScope', () => { let scope; beforeEach(() => { scope = 'enabledKeys'; mockResolvers.Mutation.currentScope(null, { scope }, { client }); }); it('sets the current scope', () => { expect(client.writeQuery).toHaveBeenCalledWith({ query: currentScopeQuery, data: { currentScope: scope }, }); }); it('resets the page to 1', () => { expect(client.writeQuery).toHaveBeenCalledWith({ query: currentPageQuery, data: { currentPage: 1 }, }); }); }); describe('disableKey', () => { it('disables the key that is pending confirmation', async () => { const key = { id: 1, title: 'hello', disablePath: '/disable', __typename: 'LocalDeployKey' }; client.readQuery.mockReturnValue({ deployKeyToRemove: key }); client.readFragment.mockReturnValue(key); mock.onPut(key.disablePath).reply(HTTP_STATUS_OK); await mockResolvers.Mutation.disableKey(null, null, { client }); expect(client.readQuery).toHaveBeenCalledWith({ query: confirmRemoveKeyQuery }); expect(client.readFragment).toHaveBeenCalledWith( expect.objectContaining({ id: `LocalDeployKey:${key.id}` }), ); expect(client.cache.evict).toHaveBeenCalledWith({ fieldName: 'deployKeyToRemove' }); expect(client.cache.evict).toHaveBeenCalledWith({ id: `LocalDeployKey:${key.id}` }); expect(client.cache.gc).toHaveBeenCalled(); }); it('does not remove the key from the cache on fail', async () => { const key = { id: 1, title: 'hello', disablePath: '/disable', __typename: 'LocalDeployKey' }; client.readQuery.mockReturnValue({ deployKeyToRemove: key }); client.readFragment.mockReturnValue(key); mock.onPut(key.disablePath).reply(HTTP_STATUS_NOT_FOUND); try { await mockResolvers.Mutation.disableKey(null, null, { client }); } catch { expect(client.readQuery).toHaveBeenCalledWith({ query: confirmRemoveKeyQuery }); expect(client.readFragment).toHaveBeenCalledWith( expect.objectContaining({ id: `LocalDeployKey:${key.id}` }), ); expect(client.cache.evict).not.toHaveBeenCalled(); expect(client.cache.gc).not.toHaveBeenCalled(); } }); }); describe('enableKey', () => { it("calls the key's enable path", async () => { const key = { id: 1, title: 'hello', enablePath: '/enable', __typename: 'LocalDeployKey' }; client.readQuery.mockReturnValue({ deployKeyToRemove: key }); client.readFragment.mockReturnValue(key); mock.onPut(key.enablePath).reply(HTTP_STATUS_OK); await mockResolvers.Mutation.enableKey(null, key, { client }); expect(client.readFragment).toHaveBeenCalledWith( expect.objectContaining({ id: `LocalDeployKey:${key.id}` }), ); expect(client.cache.evict).toHaveBeenCalledWith({ id: `LocalDeployKey:${key.id}` }); expect(client.cache.gc).toHaveBeenCalled(); }); it('does not remove the key from the cache on failure', async () => { const key = { id: 1, title: 'hello', enablePath: '/enable', __typename: 'LocalDeployKey' }; client.readQuery.mockReturnValue({ deployKeyToRemove: key }); client.readFragment.mockReturnValue(key); mock.onPut(key.enablePath).reply(HTTP_STATUS_NOT_FOUND); try { await mockResolvers.Mutation.enableKey(null, key, { client }); } catch { expect(client.readFragment).toHaveBeenCalledWith( expect.objectContaining({ id: `LocalDeployKey:${key.id}` }), ); expect(client.cache.evict).not.toHaveBeenCalled(); expect(client.cache.gc).not.toHaveBeenCalled(); } }); }); describe('confirmDisable', () => { it('sets the key to disable', () => { const key = { id: 1, title: 'hello', enablePath: '/enable', __typename: 'LocalDeployKey' }; mockResolvers.Mutation.confirmDisable(null, key, { client }); expect(client.writeQuery).toHaveBeenCalledWith({ query: confirmRemoveKeyQuery, data: { deployKeyToRemove: { id: key.id, __type: 'LocalDeployKey' } }, }); }); it('clears the value when null id is passed', () => { mockResolvers.Mutation.confirmDisable(null, { id: null }, { client }); expect(client.writeQuery).toHaveBeenCalledWith({ query: confirmRemoveKeyQuery, data: { deployKeyToRemove: null }, }); }); }); });