import '@sim/testing/mocks/executor' import { beforeEach, describe, expect, it } from 'vitest' import { TriggerBlockHandler } from '@/executor/handlers/trigger/trigger-handler' import type { ExecutionContext } from '@/executor/types' import type { SerializedBlock } from '@/serializer/types' describe('TriggerBlockHandler', () => { let handler: TriggerBlockHandler let mockContext: ExecutionContext beforeEach(() => { handler = new TriggerBlockHandler() mockContext = { workflowId: 'test-workflow-id', blockStates: new Map(), blockLogs: [], metadata: { duration: 0 }, environmentVariables: {}, decisions: { router: new Map(), condition: new Map() }, loopExecutions: new Map(), executedBlocks: new Set(), activeExecutionPath: new Set(), completedLoops: new Set(), } }) describe('canHandle', () => { it.concurrent('should handle blocks with triggers category', () => { const triggerBlock: SerializedBlock = { id: 'trigger-1', metadata: { id: 'schedule', name: 'Schedule Block', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'schedule', params: {} }, inputs: {}, outputs: {}, enabled: true, } expect(handler.canHandle(triggerBlock)).toBe(true) }) it.concurrent('should handle blocks with triggerMode enabled', () => { const gmailTriggerBlock: SerializedBlock = { id: 'gmail-1', metadata: { id: 'gmail', name: 'Gmail Block', category: 'tools' }, position: { x: 0, y: 0 }, config: { tool: 'gmail', params: { triggerMode: true } }, inputs: {}, outputs: {}, enabled: true, } expect(handler.canHandle(gmailTriggerBlock)).toBe(true) }) it.concurrent('should not handle regular tool blocks without triggerMode', () => { const toolBlock: SerializedBlock = { id: 'tool-1', metadata: { id: 'gmail', name: 'Gmail Block', category: 'tools' }, position: { x: 0, y: 0 }, config: { tool: 'gmail', params: { triggerMode: false } }, inputs: {}, outputs: {}, enabled: true, } expect(handler.canHandle(toolBlock)).toBe(false) }) it.concurrent('should not handle blocks without trigger indicators', () => { const regularBlock: SerializedBlock = { id: 'regular-1', metadata: { id: 'api', name: 'API Block', category: 'tools' }, position: { x: 0, y: 0 }, config: { tool: 'api', params: {} }, inputs: {}, outputs: {}, enabled: true, } expect(handler.canHandle(regularBlock)).toBe(false) }) it.concurrent('should handle generic webhook blocks', () => { const webhookBlock: SerializedBlock = { id: 'webhook-1', metadata: { id: 'generic_webhook', name: 'Generic Webhook', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'generic_webhook', params: {} }, inputs: {}, outputs: {}, enabled: true, } expect(handler.canHandle(webhookBlock)).toBe(true) }) }) describe('execute', () => { it.concurrent('should return inputs directly when provided', async () => { const triggerBlock: SerializedBlock = { id: 'trigger-1', metadata: { id: 'gmail', name: 'Gmail Trigger', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'gmail', params: {} }, inputs: {}, outputs: {}, enabled: true, } const triggerInputs = { email: { id: '12345', subject: 'Test Email', from: 'test@example.com', body: 'Hello world', }, timestamp: '2023-01-01T12:00:00Z', } const result = await handler.execute(mockContext, triggerBlock, triggerInputs) expect(result).toEqual(triggerInputs) }) it.concurrent('should return empty object when no inputs provided', async () => { const triggerBlock: SerializedBlock = { id: 'trigger-1', metadata: { id: 'schedule', name: 'Schedule Trigger', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'schedule', params: {} }, inputs: {}, outputs: {}, enabled: true, } const result = await handler.execute(mockContext, triggerBlock, {}) expect(result).toEqual({}) }) it.concurrent('should handle webhook payload inputs', async () => { const webhookBlock: SerializedBlock = { id: 'webhook-1', metadata: { id: 'generic_webhook', name: 'Generic Webhook', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'generic_webhook', params: {} }, inputs: {}, outputs: {}, enabled: true, } const webhookInputs = { webhook: { data: { provider: 'github', payload: { event: 'push', repo: 'test-repo' }, }, }, } const result = await handler.execute(mockContext, webhookBlock, webhookInputs) expect(result).toEqual(webhookInputs) }) it.concurrent('should handle Outlook trigger inputs', async () => { const outlookBlock: SerializedBlock = { id: 'outlook-1', metadata: { id: 'outlook', name: 'Outlook Block', category: 'tools' }, position: { x: 0, y: 0 }, config: { tool: 'outlook', params: { triggerMode: true } }, inputs: {}, outputs: {}, enabled: true, } const outlookInputs = { email: { id: 'outlook123', subject: 'Meeting Invitation', from: 'colleague@company.com', bodyPreview: 'Join us for the quarterly review...', }, timestamp: '2023-01-01T14:30:00Z', } const result = await handler.execute(mockContext, outlookBlock, outlookInputs) expect(result).toEqual(outlookInputs) }) it.concurrent('should handle schedule trigger with no inputs', async () => { const scheduleBlock: SerializedBlock = { id: 'schedule-1', metadata: { id: 'schedule', name: 'Daily Schedule', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'schedule', params: { scheduleType: 'daily' } }, inputs: {}, outputs: {}, enabled: true, } const result = await handler.execute(mockContext, scheduleBlock, {}) expect(result).toEqual({}) }) it.concurrent('should handle complex nested trigger data', async () => { const triggerBlock: SerializedBlock = { id: 'complex-trigger-1', metadata: { id: 'webhook', name: 'Complex Webhook', category: 'triggers' }, position: { x: 0, y: 0 }, config: { tool: 'webhook', params: {} }, inputs: {}, outputs: {}, enabled: true, } const complexInputs = { webhook: { data: { provider: 'github', payload: { action: 'opened', pull_request: { id: 123, title: 'Fix bug in authentication', user: { login: 'developer' }, base: { ref: 'main' }, head: { ref: 'fix-auth-bug' }, }, }, headers: { 'x-github-event': 'pull_request' }, }, }, timestamp: '2023-01-01T15:45:00Z', } const result = await handler.execute(mockContext, triggerBlock, complexInputs) expect(result).toEqual(complexInputs) }) }) describe('integration scenarios', () => { it.concurrent('should work with different trigger block types', () => { const testCases = [ { name: 'Gmail in trigger mode', block: { id: 'gmail-trigger', metadata: { id: 'gmail', category: 'tools' }, config: { tool: 'gmail', params: { triggerMode: true } }, }, shouldHandle: true, }, { name: 'Generic webhook', block: { id: 'webhook-trigger', metadata: { id: 'generic_webhook', category: 'triggers' }, config: { tool: 'generic_webhook', params: {} }, }, shouldHandle: true, }, { name: 'Schedule block', block: { id: 'schedule-trigger', metadata: { id: 'schedule', category: 'triggers' }, config: { tool: 'schedule', params: {} }, }, shouldHandle: true, }, { name: 'Regular API block', block: { id: 'api-block', metadata: { id: 'api', category: 'tools' }, config: { tool: 'api', params: {} }, }, shouldHandle: false, }, { name: 'Gmail in tool mode', block: { id: 'gmail-tool', metadata: { id: 'gmail', category: 'tools' }, config: { tool: 'gmail', params: { triggerMode: false } }, }, shouldHandle: false, }, ] testCases.forEach(({ name, block, shouldHandle }) => { const serializedBlock: SerializedBlock = { ...block, position: { x: 0, y: 0 }, inputs: {}, outputs: {}, enabled: true, } as SerializedBlock expect( handler.canHandle(serializedBlock), `${name} should ${shouldHandle ? '' : 'not '}be handled` ).toBe(shouldHandle) }) }) }) })