Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/environments/graphql/resolvers/kubernetes_spec.js')
-rw-r--r--spec/frontend/environments/graphql/resolvers/kubernetes_spec.js297
1 files changed, 235 insertions, 62 deletions
diff --git a/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js b/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js
index f244ddb01b5..4f3295442b5 100644
--- a/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js
+++ b/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js
@@ -4,6 +4,8 @@ import axios from '~/lib/utils/axios_utils';
import { resolvers } from '~/environments/graphql/resolvers';
import { CLUSTER_AGENT_ERROR_MESSAGES } from '~/environments/constants';
import k8sPodsQuery from '~/environments/graphql/queries/k8s_pods.query.graphql';
+import k8sWorkloadsQuery from '~/environments/graphql/queries/k8s_workloads.query.graphql';
+import k8sServicesQuery from '~/environments/graphql/queries/k8s_services.query.graphql';
import { k8sPodsMock, k8sServicesMock, k8sNamespacesMock } from '../mock_data';
describe('~/frontend/environments/graphql/resolvers', () => {
@@ -157,6 +159,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
});
});
describe('k8sServices', () => {
+ const client = { writeQuery: jest.fn() };
const mockServicesListFn = jest.fn().mockImplementation(() => {
return Promise.resolve({
items: k8sServicesMock,
@@ -166,49 +169,130 @@ describe('~/frontend/environments/graphql/resolvers', () => {
const mockNamespacedServicesListFn = jest.fn().mockImplementation(mockServicesListFn);
const mockAllServicesListFn = jest.fn().mockImplementation(mockServicesListFn);
- beforeEach(() => {
- jest
- .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
- .mockImplementation(mockServicesListFn);
+ describe('when k8sWatchApi feature is disabled', () => {
+ beforeEach(() => {
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1NamespacedService')
+ .mockImplementation(mockNamespacedServicesListFn);
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
+ .mockImplementation(mockAllServicesListFn);
+ });
- jest
- .spyOn(CoreV1Api.prototype, 'listCoreV1NamespacedService')
- .mockImplementation(mockNamespacedServicesListFn);
- jest
- .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
- .mockImplementation(mockAllServicesListFn);
- });
+ it('should request namespaced services from the cluster_client library if namespace is specified', async () => {
+ const services = await mockResolvers.Query.k8sServices(
+ null,
+ { configuration, namespace },
+ { client },
+ );
- it('should request namespaced services from the cluster_client library if namespace is specified', async () => {
- const services = await mockResolvers.Query.k8sServices(null, { configuration, namespace });
+ expect(mockNamespacedServicesListFn).toHaveBeenCalledWith({ namespace });
+ expect(mockAllServicesListFn).not.toHaveBeenCalled();
- expect(mockNamespacedServicesListFn).toHaveBeenCalledWith({ namespace });
- expect(mockAllServicesListFn).not.toHaveBeenCalled();
+ expect(services).toEqual(k8sServicesMock);
+ });
+ it('should request all services from the cluster_client library if namespace is not specified', async () => {
+ const services = await mockResolvers.Query.k8sServices(
+ null,
+ {
+ configuration,
+ namespace: '',
+ },
+ { client },
+ );
+
+ expect(mockServicesListFn).toHaveBeenCalled();
+ expect(mockNamespacedServicesListFn).not.toHaveBeenCalled();
+
+ expect(services).toEqual(k8sServicesMock);
+ });
+ it('should throw an error if the API call fails', async () => {
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
+ .mockRejectedValue(new Error('API error'));
- expect(services).toEqual(k8sServicesMock);
+ await expect(
+ mockResolvers.Query.k8sServices(null, { configuration }, { client }),
+ ).rejects.toThrow('API error');
+ });
});
- it('should request all services from the cluster_client library if namespace is not specified', async () => {
- const services = await mockResolvers.Query.k8sServices(null, {
- configuration,
- namespace: '',
+
+ describe('when k8sWatchApi feature is enabled', () => {
+ const mockWatcher = WatchApi.prototype;
+ const mockServicesListWatcherFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve(mockWatcher);
+ });
+
+ const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => {
+ if (eventName === 'data') {
+ callback([]);
+ }
});
- expect(mockServicesListFn).toHaveBeenCalled();
- expect(mockNamespacedServicesListFn).not.toHaveBeenCalled();
+ describe('when the services data is present', () => {
+ beforeEach(() => {
+ gon.features = { k8sWatchApi: true };
- expect(services).toEqual(k8sServicesMock);
- });
- it('should throw an error if the API call fails', async () => {
- jest
- .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
- .mockRejectedValue(new Error('API error'));
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1NamespacedService')
+ .mockImplementation(mockNamespacedServicesListFn);
+ jest
+ .spyOn(CoreV1Api.prototype, 'listCoreV1ServiceForAllNamespaces')
+ .mockImplementation(mockAllServicesListFn);
+ jest
+ .spyOn(mockWatcher, 'subscribeToStream')
+ .mockImplementation(mockServicesListWatcherFn);
+ jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn);
+ });
+
+ it('should request namespaced services from the cluster_client library if namespace is specified', async () => {
+ await mockResolvers.Query.k8sServices(null, { configuration, namespace }, { client });
+
+ expect(mockServicesListWatcherFn).toHaveBeenCalledWith(
+ `/api/v1/namespaces/${namespace}/services`,
+ {
+ watch: true,
+ },
+ );
+ });
+ it('should request all services from the cluster_client library if namespace is not specified', async () => {
+ await mockResolvers.Query.k8sServices(null, { configuration, namespace: '' }, { client });
+
+ expect(mockServicesListWatcherFn).toHaveBeenCalledWith(`/api/v1/services`, {
+ watch: true,
+ });
+ });
+ it('should update cache with the new data when received from the library', async () => {
+ await mockResolvers.Query.k8sServices(null, { configuration, namespace: '' }, { client });
+
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: k8sServicesQuery,
+ variables: { configuration, namespace: '' },
+ data: { k8sServices: [] },
+ });
+ });
+ });
+
+ it('should not watch pods from the cluster_client library when the services data is not present', async () => {
+ jest.spyOn(CoreV1Api.prototype, 'listCoreV1NamespacedService').mockImplementation(
+ jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: [],
+ });
+ }),
+ );
- await expect(mockResolvers.Query.k8sServices(null, { configuration })).rejects.toThrow(
- 'API error',
- );
+ await mockResolvers.Query.k8sServices(null, { configuration, namespace }, { client });
+
+ expect(mockServicesListWatcherFn).not.toHaveBeenCalled();
+ });
});
});
describe('k8sWorkloads', () => {
+ const client = {
+ readQuery: jest.fn(() => ({ k8sWorkloads: {} })),
+ writeQuery: jest.fn(),
+ };
const emptyImplementation = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
@@ -250,48 +334,137 @@ describe('~/frontend/environments/graphql/resolvers', () => {
{ method: 'listBatchV1CronJobForAllNamespaces', api: BatchV1Api, spy: mockAllCronJob },
];
- beforeEach(() => {
- [...namespacedMocks, ...allMocks].forEach((workloadMock) => {
- jest
- .spyOn(workloadMock.api.prototype, workloadMock.method)
- .mockImplementation(workloadMock.spy);
+ describe('when k8sWatchApi feature is disabled', () => {
+ beforeEach(() => {
+ [...namespacedMocks, ...allMocks].forEach((workloadMock) => {
+ jest
+ .spyOn(workloadMock.api.prototype, workloadMock.method)
+ .mockImplementation(workloadMock.spy);
+ });
});
- });
- it('should request namespaced workload types from the cluster_client library if namespace is specified', async () => {
- await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace });
+ it('should request namespaced workload types from the cluster_client library if namespace is specified', async () => {
+ await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace }, { client });
- namespacedMocks.forEach((workloadMock) => {
- expect(workloadMock.spy).toHaveBeenCalledWith({ namespace });
+ namespacedMocks.forEach((workloadMock) => {
+ expect(workloadMock.spy).toHaveBeenCalledWith({ namespace });
+ });
});
- });
- it('should request all workload types from the cluster_client library if namespace is not specified', async () => {
- await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace: '' });
+ it('should request all workload types from the cluster_client library if namespace is not specified', async () => {
+ await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace: '' }, { client });
- allMocks.forEach((workloadMock) => {
- expect(workloadMock.spy).toHaveBeenCalled();
+ allMocks.forEach((workloadMock) => {
+ expect(workloadMock.spy).toHaveBeenCalled();
+ });
});
- });
- it('should pass fulfilled calls data if one of the API calls fail', async () => {
- jest
- .spyOn(AppsV1Api.prototype, 'listAppsV1DeploymentForAllNamespaces')
- .mockRejectedValue(new Error('API error'));
-
- await expect(
- mockResolvers.Query.k8sWorkloads(null, { configuration }),
- ).resolves.toBeDefined();
- });
- it('should throw an error if all the API calls fail', async () => {
- [...allMocks].forEach((workloadMock) => {
+ it('should pass fulfilled calls data if one of the API calls fail', async () => {
jest
- .spyOn(workloadMock.api.prototype, workloadMock.method)
+ .spyOn(AppsV1Api.prototype, 'listAppsV1DeploymentForAllNamespaces')
.mockRejectedValue(new Error('API error'));
+
+ await expect(
+ mockResolvers.Query.k8sWorkloads(null, { configuration }, { client }),
+ ).resolves.toBeDefined();
+ });
+ it('should throw an error if all the API calls fail', async () => {
+ [...allMocks].forEach((workloadMock) => {
+ jest
+ .spyOn(workloadMock.api.prototype, workloadMock.method)
+ .mockRejectedValue(new Error('API error'));
+ });
+
+ await expect(
+ mockResolvers.Query.k8sWorkloads(null, { configuration }, { client }),
+ ).rejects.toThrow('API error');
+ });
+ });
+ describe('when k8sWatchApi feature is enabled', () => {
+ const mockDeployment = jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ kind: 'DeploymentList',
+ apiVersion: 'apps/v1',
+ items: [
+ {
+ status: {
+ conditions: [],
+ },
+ },
+ ],
+ });
+ });
+ const mockWatcher = WatchApi.prototype;
+ const mockDeploymentsListWatcherFn = jest.fn().mockImplementation(() => {
+ return Promise.resolve(mockWatcher);
+ });
+
+ const mockOnDataFn = jest.fn().mockImplementation((eventName, callback) => {
+ if (eventName === 'data') {
+ callback([]);
+ }
});
- await expect(mockResolvers.Query.k8sWorkloads(null, { configuration })).rejects.toThrow(
- 'API error',
- );
+ describe('when the deployments data is present', () => {
+ beforeEach(() => {
+ gon.features = { k8sWatchApi: true };
+
+ jest
+ .spyOn(AppsV1Api.prototype, 'listAppsV1NamespacedDeployment')
+ .mockImplementation(mockDeployment);
+ jest
+ .spyOn(AppsV1Api.prototype, 'listAppsV1DeploymentForAllNamespaces')
+ .mockImplementation(mockDeployment);
+ jest
+ .spyOn(mockWatcher, 'subscribeToStream')
+ .mockImplementation(mockDeploymentsListWatcherFn);
+ jest.spyOn(mockWatcher, 'on').mockImplementation(mockOnDataFn);
+ });
+
+ it('should request namespaced deployments from the cluster_client library if namespace is specified', async () => {
+ await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace }, { client });
+
+ expect(mockDeploymentsListWatcherFn).toHaveBeenCalledWith(
+ `/apis/apps/v1/namespaces/${namespace}/deployments`,
+ {
+ watch: true,
+ },
+ );
+ });
+ it('should request all deployments from the cluster_client library if namespace is not specified', async () => {
+ await mockResolvers.Query.k8sWorkloads(
+ null,
+ { configuration, namespace: '' },
+ { client },
+ );
+
+ expect(mockDeploymentsListWatcherFn).toHaveBeenCalledWith(`/apis/apps/v1/deployments`, {
+ watch: true,
+ });
+ });
+ it('should update cache with the new data when received from the library', async () => {
+ await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace }, { client });
+
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: k8sWorkloadsQuery,
+ variables: { configuration, namespace },
+ data: { k8sWorkloads: { DeploymentList: [] } },
+ });
+ });
+ });
+
+ it('should not watch deployments from the cluster_client library when the deployments data is not present', async () => {
+ jest.spyOn(AppsV1Api.prototype, 'listAppsV1NamespacedDeployment').mockImplementation(
+ jest.fn().mockImplementation(() => {
+ return Promise.resolve({
+ items: [],
+ });
+ }),
+ );
+
+ await mockResolvers.Query.k8sWorkloads(null, { configuration, namespace }, { client });
+
+ expect(mockDeploymentsListWatcherFn).not.toHaveBeenCalled();
+ });
});
});
describe('k8sNamespaces', () => {