chat-trigger-validation.test.tsβ’9.9 kB
/**
* Integration Tests: Chat Trigger Validation
*
* Tests Chat Trigger validation against real n8n instance.
*/
import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
import { createTestContext, TestContext, createTestWorkflowName } from '../n8n-api/utils/test-context';
import { getTestN8nClient } from '../n8n-api/utils/n8n-client';
import { N8nApiClient } from '../../../src/services/n8n-api-client';
import { cleanupOrphanedWorkflows } from '../n8n-api/utils/cleanup-helpers';
import { createMcpContext } from '../n8n-api/utils/mcp-context';
import { InstanceContext } from '../../../src/types/instance-context';
import { handleValidateWorkflow } from '../../../src/mcp/handlers-n8n-manager';
import { getNodeRepository, closeNodeRepository } from '../n8n-api/utils/node-repository';
import { NodeRepository } from '../../../src/database/node-repository';
import { ValidationResponse } from '../n8n-api/types/mcp-responses';
import {
createChatTriggerNode,
createAIAgentNode,
createLanguageModelNode,
createRespondNode,
createAIConnection,
createMainConnection,
mergeConnections,
createAIWorkflow
} from './helpers';
import { WorkflowNode } from '../../../src/types/n8n-api';
describe('Integration: Chat Trigger Validation', () => {
let context: TestContext;
let client: N8nApiClient;
let mcpContext: InstanceContext;
let repository: NodeRepository;
beforeEach(async () => {
context = createTestContext();
client = getTestN8nClient();
mcpContext = createMcpContext();
repository = await getNodeRepository();
});
afterEach(async () => {
await context.cleanup();
});
afterAll(async () => {
await closeNodeRepository();
if (!process.env.CI) {
await cleanupOrphanedWorkflows();
}
});
// ======================================================================
// TEST 1: Streaming to Non-AI-Agent
// ======================================================================
it('should detect streaming to non-AI-Agent', async () => {
const chatTrigger = createChatTriggerNode({
name: 'Chat Trigger',
responseMode: 'streaming'
});
// Regular node (not AI Agent)
const regularNode: WorkflowNode = {
id: 'set-1',
name: 'Set',
type: 'n8n-nodes-base.set',
typeVersion: 3.4,
position: [450, 300],
parameters: {
assignments: {
assignments: []
}
}
};
const workflow = createAIWorkflow(
[chatTrigger, regularNode],
createMainConnection('Chat Trigger', 'Set'),
{
name: createTestWorkflowName('Chat Trigger - Wrong Target'),
tags: ['mcp-integration-test', 'ai-validation']
}
);
const created = await client.createWorkflow(workflow);
context.trackWorkflow(created.id!);
const response = await handleValidateWorkflow(
{ id: created.id },
repository,
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as ValidationResponse;
expect(data.valid).toBe(false);
expect(data.errors).toBeDefined();
const errorCodes = data.errors!.map(e => e.details?.code || e.code);
expect(errorCodes).toContain('STREAMING_WRONG_TARGET');
const errorMessages = data.errors!.map(e => e.message).join(' ');
expect(errorMessages).toMatch(/streaming.*AI Agent/i);
});
// ======================================================================
// TEST 2: Missing Connections
// ======================================================================
it('should detect missing connections', async () => {
const chatTrigger = createChatTriggerNode({
name: 'Chat Trigger'
});
const workflow = createAIWorkflow(
[chatTrigger],
{}, // No connections
{
name: createTestWorkflowName('Chat Trigger - No Connections'),
tags: ['mcp-integration-test', 'ai-validation']
}
);
const created = await client.createWorkflow(workflow);
context.trackWorkflow(created.id!);
const response = await handleValidateWorkflow(
{ id: created.id },
repository,
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as ValidationResponse;
expect(data.valid).toBe(false);
expect(data.errors).toBeDefined();
const errorCodes = data.errors!.map(e => e.details?.code || e.code);
expect(errorCodes).toContain('MISSING_CONNECTIONS');
});
// ======================================================================
// TEST 3: Valid Streaming Setup
// ======================================================================
it('should validate valid streaming setup', async () => {
const chatTrigger = createChatTriggerNode({
name: 'Chat Trigger',
responseMode: 'streaming'
});
const languageModel = createLanguageModelNode('openai', {
name: 'OpenAI Chat Model'
});
const agent = createAIAgentNode({
name: 'AI Agent',
text: 'You are a helpful assistant'
// No main output connections - streaming mode
});
const workflow = createAIWorkflow(
[chatTrigger, languageModel, agent],
mergeConnections(
createMainConnection('Chat Trigger', 'AI Agent'),
createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel')
// NO main output from AI Agent
),
{
name: createTestWorkflowName('Chat Trigger - Valid Streaming'),
tags: ['mcp-integration-test', 'ai-validation']
}
);
const created = await client.createWorkflow(workflow);
context.trackWorkflow(created.id!);
const response = await handleValidateWorkflow(
{ id: created.id },
repository,
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as ValidationResponse;
expect(data.valid).toBe(true);
expect(data.errors).toBeUndefined();
expect(data.summary.errorCount).toBe(0);
});
// ======================================================================
// TEST 4: LastNode Mode (Default)
// ======================================================================
it('should validate lastNode mode with AI Agent', async () => {
const chatTrigger = createChatTriggerNode({
name: 'Chat Trigger',
responseMode: 'lastNode'
});
const languageModel = createLanguageModelNode('openai', {
name: 'OpenAI Chat Model'
});
const agent = createAIAgentNode({
name: 'AI Agent',
text: 'You are a helpful assistant'
});
const respond = createRespondNode({
name: 'Respond to Webhook'
});
const workflow = createAIWorkflow(
[chatTrigger, languageModel, agent, respond],
mergeConnections(
createMainConnection('Chat Trigger', 'AI Agent'),
createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'),
createMainConnection('AI Agent', 'Respond to Webhook')
),
{
name: createTestWorkflowName('Chat Trigger - LastNode Mode'),
tags: ['mcp-integration-test', 'ai-validation']
}
);
const created = await client.createWorkflow(workflow);
context.trackWorkflow(created.id!);
const response = await handleValidateWorkflow(
{ id: created.id },
repository,
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as ValidationResponse;
// Should be valid (lastNode mode allows main output)
expect(data.valid).toBe(true);
// May have info suggestion about using streaming
if (data.info) {
const streamingSuggestion = data.info.find((i: any) =>
i.message.toLowerCase().includes('streaming')
);
// This is optional - just checking the suggestion exists if present
if (streamingSuggestion) {
expect(streamingSuggestion.severity).toBe('info');
}
}
});
// ======================================================================
// TEST 5: Streaming Agent with Output Connection (Error)
// ======================================================================
it('should detect streaming agent with output connection', async () => {
const chatTrigger = createChatTriggerNode({
name: 'Chat Trigger',
responseMode: 'streaming'
});
const languageModel = createLanguageModelNode('openai', {
name: 'OpenAI Chat Model'
});
const agent = createAIAgentNode({
name: 'AI Agent',
text: 'You are a helpful assistant'
});
const respond = createRespondNode({
name: 'Respond to Webhook'
});
const workflow = createAIWorkflow(
[chatTrigger, languageModel, agent, respond],
mergeConnections(
createMainConnection('Chat Trigger', 'AI Agent'),
createAIConnection('OpenAI Chat Model', 'AI Agent', 'ai_languageModel'),
createMainConnection('AI Agent', 'Respond to Webhook') // ERROR in streaming mode
),
{
name: createTestWorkflowName('Chat Trigger - Streaming With Output'),
tags: ['mcp-integration-test', 'ai-validation']
}
);
const created = await client.createWorkflow(workflow);
context.trackWorkflow(created.id!);
const response = await handleValidateWorkflow(
{ id: created.id },
repository,
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as ValidationResponse;
expect(data.valid).toBe(false);
expect(data.errors).toBeDefined();
// Should detect streaming agent has output
const streamingErrors = data.errors!.filter(e => {
const code = e.details?.code || e.code;
return code === 'STREAMING_AGENT_HAS_OUTPUT' ||
e.message.toLowerCase().includes('streaming');
});
expect(streamingErrors.length).toBeGreaterThan(0);
});
});