]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18447 Create webhook service mock & shared mock data
author7PH <benjamin.raymond@sonarsource.com>
Tue, 11 Jul 2023 15:23:12 +0000 (17:23 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 18 Jul 2023 20:03:23 +0000 (20:03 +0000)
server/sonar-web/src/main/js/api/mocks/WebhooksMock.ts [new file with mode: 0644]
server/sonar-web/src/main/js/api/mocks/data/ids.ts
server/sonar-web/src/main/js/api/mocks/data/webhooks.ts [new file with mode: 0644]
server/sonar-web/src/main/js/api/webhooks.ts
server/sonar-web/src/main/js/helpers/mocks/webhook.ts
server/sonar-web/src/main/js/types/webhook.ts

diff --git a/server/sonar-web/src/main/js/api/mocks/WebhooksMock.ts b/server/sonar-web/src/main/js/api/mocks/WebhooksMock.ts
new file mode 100644 (file)
index 0000000..0ae80cf
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import {
+  createWebhook,
+  deleteWebhook,
+  getDelivery,
+  searchDeliveries,
+  searchWebhooks,
+  updateWebhook,
+} from '../../api/webhooks';
+import { mockWebhook } from '../../helpers/mocks/webhook';
+import { mockPaging } from '../../helpers/testMocks';
+import {
+  WebhookCreatePayload,
+  WebhookResponse,
+  WebhookSearchDeliveriesPayload,
+  WebhookUpdatePayload,
+} from '../../types/webhook';
+import { deliveries, mockFullWebhookDeliveries, mockFullWebhookList } from './data/webhooks';
+
+jest.mock('../../api/webhooks');
+
+export default class WebhooksMock {
+  static readonly DEFAULT_PAGE_SIZE = 10;
+
+  project?: string;
+  webhooks: Array<WebhookResponse>;
+
+  constructor(project?: string) {
+    this.project = project;
+    this.webhooks = mockFullWebhookList(project);
+
+    jest.mocked(createWebhook).mockImplementation(this.handleCreateWebhook);
+    jest.mocked(searchWebhooks).mockImplementation(this.handleSearchWebhooks);
+    jest.mocked(updateWebhook).mockImplementation(this.handleUpdateWebhook);
+    jest.mocked(deleteWebhook).mockImplementation(this.handleDeleteWebhook);
+    jest.mocked(searchDeliveries).mockImplementation(this.handleSearchDeliveries);
+    jest.mocked(getDelivery).mockImplementation(this.handleGetDelivery);
+  }
+
+  reset = (project?: string) => {
+    this.project = project;
+    this.webhooks = mockFullWebhookList(project);
+  };
+
+  response = <T>(data: T): Promise<T> => {
+    return Promise.resolve(data);
+  };
+
+  addWebhook = (...webhooks: WebhookResponse[]) => {
+    this.webhooks.push(...webhooks);
+  };
+
+  handleCreateWebhook = (data: WebhookCreatePayload) => {
+    const webhook = mockWebhook({
+      name: data.name,
+      url: data.url,
+      hasSecret: Boolean(data.secret),
+    });
+    return this.response({
+      webhook,
+    });
+  };
+
+  handleSearchWebhooks = ({ project }: { project?: string }) => {
+    if (project !== this.project) {
+      throw new Error(
+        'You are asking for webhooks of a project that is not mocked. Reset first the mock with the correct project'
+      );
+    }
+    return this.response({
+      webhooks: this.webhooks,
+    });
+  };
+
+  handleUpdateWebhook = (data: WebhookUpdatePayload) => {
+    const webhook = this.webhooks.find((webhook) => webhook.key === data.webhook);
+    if (!webhook) {
+      return Promise.reject(new Error('Webhook not found'));
+    }
+    webhook.hasSecret = Boolean(data.secret);
+    webhook.name = data.name;
+    webhook.url = data.url;
+    return this.response(undefined);
+  };
+
+  handleDeleteWebhook = ({ webhook }: { webhook: string }) => {
+    const index = this.webhooks.findIndex((w) => w.key === webhook);
+    if (index === -1) {
+      return Promise.reject(new Error('Webhook not found'));
+    }
+    this.webhooks.splice(index, 1);
+    return this.response(undefined);
+  };
+
+  handleSearchDeliveries = ({
+    webhook,
+    ps = WebhooksMock.DEFAULT_PAGE_SIZE,
+    p = 1,
+  }: WebhookSearchDeliveriesPayload) => {
+    const deliveries = mockFullWebhookDeliveries(webhook);
+    const start = (p - 1) * ps;
+    const end = start + ps;
+    const paging = mockPaging({ pageIndex: p, pageSize: ps, total: deliveries.length });
+    const page = deliveries.slice(start, end);
+    return this.response({
+      deliveries: page,
+      paging,
+    });
+  };
+
+  handleGetDelivery = ({ deliveryId }: { deliveryId: string }) => {
+    const delivery = deliveries.find((delivery) => delivery.id === deliveryId);
+    if (!delivery) {
+      return Promise.reject(new Error('Delivery not found'));
+    }
+    return this.response({
+      delivery: {
+        ...delivery,
+        payload: JSON.stringify({ id: delivery.id }),
+      },
+    });
+  };
+}
index db4aa491ecb1d9012187b0e6a7e5458db295194e..075d5a0ef08303c191dc0f0a69556cdb1871f488 100644 (file)
@@ -114,3 +114,11 @@ export const STANDARDS_TO_RULES: Partial<{
     '297': [RULE_1, RULE_4],
   },
 };
+
+// Webhooks.
+export const WEBHOOK_GLOBAL_1 = 'global-webhook1';
+export const WEBHOOK_GLOBAL_1_LATEST_DELIVERY_ID = 'global-delivery1';
+export const WEBHOOK_GLOBAL_2 = 'global-webhook2';
+export const WEBHOOK_PROJECT_1 = 'project1';
+export const WEBHOOK_PROJECT_1_1 = 'project1-webhook1';
+export const WEBHOOK_PROJECT_1_2 = 'project1-webhook2';
diff --git a/server/sonar-web/src/main/js/api/mocks/data/webhooks.ts b/server/sonar-web/src/main/js/api/mocks/data/webhooks.ts
new file mode 100644 (file)
index 0000000..cc8a7aa
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { cloneDeep } from 'lodash';
+import { mockComponent } from '../../../helpers/mocks/component';
+import { mockWebhook, mockWebhookDelivery } from '../../../helpers/mocks/webhook';
+import { ComponentQualifier } from '../../../types/component';
+import { WebhookDelivery, WebhookResponse } from '../../../types/webhook';
+import {
+  WEBHOOK_GLOBAL_1,
+  WEBHOOK_GLOBAL_1_LATEST_DELIVERY_ID,
+  WEBHOOK_GLOBAL_2,
+  WEBHOOK_PROJECT_1,
+  WEBHOOK_PROJECT_1_1,
+  WEBHOOK_PROJECT_1_2,
+} from './ids';
+
+export const webhookProject = mockComponent({
+  key: WEBHOOK_PROJECT_1,
+  qualifier: ComponentQualifier.Project,
+});
+
+const globalWebhook1Deliveries = [
+  mockWebhookDelivery({
+    at: '2019-06-24T09:45:52+0200',
+    id: WEBHOOK_GLOBAL_1_LATEST_DELIVERY_ID,
+  }),
+  ...Array.from({ length: 15 }).map((_, i) =>
+    mockWebhookDelivery({
+      id: `global-webhook-1-delivery-${i}`,
+      at: `2019-06-${(23 - i).toString().padStart(2, '0')}T09:45:52+0200`,
+      httpStatus: i % 2 === 0 ? 200 : undefined,
+      success: i % 2 === 0,
+      durationMs: 1000 + i * 100,
+    })
+  ),
+];
+
+const project1Webhook1Deliveries = [
+  mockWebhookDelivery({
+    id: 'project-1-delivery-1',
+    at: '2019-06-24T09:45:52+0200',
+  }),
+];
+
+export const deliveries = [...globalWebhook1Deliveries, ...project1Webhook1Deliveries];
+
+export const webhooks: Record<string, Array<WebhookResponse>> = {
+  global: [
+    mockWebhook({
+      key: WEBHOOK_GLOBAL_1,
+      name: 'Global webhook 1',
+      url: 'https://example.com/1',
+      latestDelivery: globalWebhook1Deliveries[0],
+    }),
+    mockWebhook({
+      key: WEBHOOK_GLOBAL_2,
+      name: 'Global webhook 2',
+      hasSecret: true,
+      url: 'https://example.com/2',
+    }),
+  ],
+  [webhookProject.key]: [
+    mockWebhook({
+      key: WEBHOOK_PROJECT_1_1,
+      name: 'Project 1 webhook 1',
+      url: 'https://example.com/1',
+      latestDelivery: project1Webhook1Deliveries[0],
+    }),
+    mockWebhook({
+      key: WEBHOOK_PROJECT_1_2,
+      name: 'Project 1 webhook 2',
+      url: 'https://example.com/2',
+    }),
+  ],
+};
+
+export function mockFullWebhookList(project?: string): Array<WebhookResponse> {
+  return cloneDeep(webhooks[project ?? 'global'] ?? []);
+}
+
+export function mockFullWebhookDeliveries(webhook?: string): Array<WebhookDelivery> {
+  return cloneDeep(webhook === WEBHOOK_GLOBAL_1 ? globalWebhook1Deliveries : []);
+}
index b2965b03faf9d6bf3b513cedaf9b435bff1eee45..f3db07d63ca13ca6af7f5cdf463fba56902ab4ef 100644 (file)
@@ -24,6 +24,7 @@ import {
   WebhookCreatePayload,
   WebhookDelivery,
   WebhookResponse,
+  WebhookSearchDeliveriesPayload,
   WebhookUpdatePayload,
 } from '../types/webhook';
 
@@ -45,13 +46,7 @@ export function updateWebhook(data: WebhookUpdatePayload): Promise<void | Respon
   return post('/api/webhooks/update', data).catch(throwGlobalError);
 }
 
-export function searchDeliveries(data: {
-  ceTaskId?: string;
-  componentKey?: string;
-  webhook?: string;
-  p?: number;
-  ps?: number;
-}): Promise<{
+export function searchDeliveries(data: WebhookSearchDeliveriesPayload): Promise<{
   deliveries: WebhookDelivery[];
   paging: Paging;
 }> {
index 8e64f1d8df198dcf0cc284ea238aaeb60a9e9279..877600c365998a9d6e71721b8079d1c98a9f7538 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { WebhookDelivery } from '../../types/webhook';
+import { WebhookDelivery, WebhookResponse } from '../../types/webhook';
 import { HttpStatus } from '../request';
 
 export function mockWebhookDelivery(overrides: Partial<WebhookDelivery> = {}): WebhookDelivery {
@@ -30,3 +30,13 @@ export function mockWebhookDelivery(overrides: Partial<WebhookDelivery> = {}): W
     ...overrides,
   };
 }
+
+export function mockWebhook(overrides: Partial<WebhookResponse> = {}): WebhookResponse {
+  return {
+    hasSecret: false,
+    key: 'webhook1',
+    name: 'name',
+    url: 'http://example.com',
+    ...overrides,
+  };
+}
index 0ddc6003ad9eef1176fcc352b3da7cb6d587f365..8c90425fa0f7c2cc4b7c04abefbec5fd779b9f3c 100644 (file)
@@ -47,3 +47,11 @@ export interface WebhookDelivery {
   id: string;
   success: boolean;
 }
+
+export type WebhookSearchDeliveriesPayload = {
+  ceTaskId?: string;
+  componentKey?: string;
+  webhook?: string;
+  p?: number;
+  ps?: number;
+};