From 39f9a58200e9ddbfe265cae7eb42bcf305e042f7 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 31 Oct 2024 13:14:40 +0000 Subject: [PATCH] refactor(flow): split delete method and write test --- packages/backend/src/models/flow.js | 26 ++++- packages/backend/src/models/flow.test.js | 126 ++++++++++++++++++++++- 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/models/flow.js b/packages/backend/src/models/flow.js index 9b9a8826..56744396 100644 --- a/packages/backend/src/models/flow.js +++ b/packages/backend/src/models/flow.js @@ -192,7 +192,7 @@ class Flow extends Base { return createdStep; } - async delete() { + async unregisterWebhook() { const triggerStep = await this.getTriggerStep(); const trigger = await triggerStep?.getTriggerCommand(); @@ -213,15 +213,33 @@ class Flow extends Base { ); } } + } + async deleteExecutionSteps() { const executionIds = ( await this.$relatedQuery('executions').select('executions.id') ).map((execution) => execution.id); - await ExecutionStep.query().delete().whereIn('execution_id', executionIds); + return await ExecutionStep.query() + .delete() + .whereIn('execution_id', executionIds); + } + + async deleteExecutions() { + return await this.$relatedQuery('executions').delete(); + } + + async deleteSteps() { + return await this.$relatedQuery('steps').delete(); + } + + async delete() { + await this.unregisterWebhook(); + + await this.deleteExecutionSteps(); + await this.deleteExecutions(); + await this.deleteSteps(); - await this.$relatedQuery('executions').delete(); - await this.$relatedQuery('steps').delete(); await this.$query().delete(); } diff --git a/packages/backend/src/models/flow.test.js b/packages/backend/src/models/flow.test.js index cfaec443..7ebc05a1 100644 --- a/packages/backend/src/models/flow.test.js +++ b/packages/backend/src/models/flow.test.js @@ -5,9 +5,11 @@ import Base from './base.js'; import Step from './step.js'; import Execution from './execution.js'; import Telemetry from '../helpers/telemetry/index.js'; +import * as globalVariableModule from '../helpers/global-variable.js'; import { createFlow } from '../../test/factories/flow.js'; import { createStep } from '../../test/factories/step.js'; import { createExecution } from '../../test/factories/execution.js'; +import { createExecutionStep } from '../../test/factories/execution-step.js'; describe('Flow model', () => { it('tableName should return correct name', () => { @@ -309,7 +311,129 @@ describe('Flow model', () => { expect(refetchedActionStep.position).toBe(3); }); - it.todo('delete'); + describe('unregisterWebhook', () => { + it('should unregister webhook on remote when supported', async () => { + const flow = await createFlow(); + const triggerStep = await createStep({ + flowId: flow.id, + appKey: 'typeform', + key: 'new-entry', + type: 'trigger', + }); + + const unregisterHookSpy = vi.fn().mockResolvedValue(); + + vi.spyOn(Step.prototype, 'getTriggerCommand').mockResolvedValue({ + type: 'webhook', + unregisterHook: unregisterHookSpy, + }); + + const globalVariableSpy = vi + .spyOn(globalVariableModule, 'default') + .mockResolvedValue('global-variable'); + + await flow.unregisterWebhook(); + + expect(unregisterHookSpy).toHaveBeenCalledWith('global-variable'); + expect(globalVariableSpy).toHaveBeenCalledWith({ + flow, + step: triggerStep, + connection: undefined, + app: await triggerStep.getApp(), + }); + }); + + it('should silently fail when unregistration fails', async () => { + const flow = await createFlow(); + await createStep({ + flowId: flow.id, + appKey: 'typeform', + key: 'new-entry', + type: 'trigger', + }); + + const unregisterHookSpy = vi.fn().mockRejectedValue(new Error()); + + vi.spyOn(Step.prototype, 'getTriggerCommand').mockResolvedValue({ + type: 'webhook', + unregisterHook: unregisterHookSpy, + }); + + expect(await flow.unregisterWebhook()).toBe(undefined); + expect(unregisterHookSpy).toHaveBeenCalledOnce(); + }); + + it('should do nothing when trigger step is not webhook', async () => { + const flow = await createFlow(); + await createStep({ + flowId: flow.id, + type: 'trigger', + }); + + const unregisterHookSpy = vi.fn().mockRejectedValue(new Error()); + + expect(await flow.unregisterWebhook()).toBe(undefined); + expect(unregisterHookSpy).not.toHaveBeenCalled(); + }); + }); + + it('deleteExecutionSteps should delete related execution steps', async () => { + const flow = await createFlow(); + const execution = await createExecution({ flowId: flow.id }); + const firstExecutionStep = await createExecutionStep({ + executionId: execution.id, + }); + const secondExecutionStep = await createExecutionStep({ + executionId: execution.id, + }); + + await flow.deleteExecutionSteps(); + + expect(await firstExecutionStep.$query()).toBe(undefined); + expect(await secondExecutionStep.$query()).toBe(undefined); + }); + + it('deleteExecutions should delete related executions', async () => { + const flow = await createFlow(); + const firstExecution = await createExecution({ flowId: flow.id }); + const secondExecution = await createExecution({ flowId: flow.id }); + + await flow.deleteExecutions(); + + expect(await firstExecution.$query()).toBe(undefined); + expect(await secondExecution.$query()).toBe(undefined); + }); + + it('deleteSteps should delete related steps', async () => { + const flow = await createFlow(); + await flow.createInitialSteps(); + await flow.deleteSteps(); + + expect(await flow.$relatedQuery('steps')).toStrictEqual([]); + }); + + it('delete should delete the flow with its relations', async () => { + const flow = await createFlow(); + + const unregisterWebhookSpy = vi + .spyOn(flow, 'unregisterWebhook') + .mockResolvedValue(); + const deleteExecutionStepsSpy = vi + .spyOn(flow, 'deleteExecutionSteps') + .mockResolvedValue(); + const deleteExecutionsSpy = vi + .spyOn(flow, 'deleteExecutions') + .mockResolvedValue(); + const deleteStepsSpy = vi.spyOn(flow, 'deleteSteps').mockResolvedValue(); + + await flow.delete(); + + expect(unregisterWebhookSpy).toHaveBeenCalledOnce(); + expect(deleteExecutionStepsSpy).toHaveBeenCalledOnce(); + expect(deleteExecutionsSpy).toHaveBeenCalledOnce(); + expect(deleteStepsSpy).toHaveBeenCalledOnce(); + expect(await flow.$query()).toBe(undefined); + }); it.todo('duplicateFor');