Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
180 lines
4.9 KiB
TypeScript
180 lines
4.9 KiB
TypeScript
/**
|
|
* Test Case Generator
|
|
*
|
|
* Generates test cases for workflow evaluation using LLM with structured output.
|
|
* For default test cases, see fixtures/default-prompts.csv.
|
|
*/
|
|
|
|
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
import { z } from 'zod';
|
|
|
|
/**
|
|
* Options for test case generation.
|
|
*/
|
|
export interface TestCaseGeneratorOptions {
|
|
/** Number of test cases to generate (default: 10) */
|
|
count?: number;
|
|
/** Focus areas for generation */
|
|
focus?: string;
|
|
/** Complexity distribution */
|
|
complexity?: 'balanced' | 'simple' | 'complex';
|
|
}
|
|
|
|
/**
|
|
* A test case generated by the LLM.
|
|
*/
|
|
export interface GeneratedTestCase {
|
|
id: string;
|
|
name: string;
|
|
summary: string;
|
|
prompt: string;
|
|
}
|
|
|
|
/**
|
|
* Test case generator interface.
|
|
*/
|
|
export interface TestCaseGenerator {
|
|
/** Generate test cases */
|
|
generate(): Promise<GeneratedTestCase[]>;
|
|
}
|
|
|
|
/**
|
|
* Zod schema for structured output.
|
|
*/
|
|
const generatedTestCasesSchema = z.object({
|
|
testCases: z.array(
|
|
z.object({
|
|
id: z.string().describe('Unique identifier (e.g., "test_001")'),
|
|
name: z.string().describe('Short descriptive title'),
|
|
summary: z.string().describe('Brief description of what the workflow does'),
|
|
prompt: z.string().describe('User-facing prompt for workflow generation'),
|
|
}),
|
|
),
|
|
});
|
|
|
|
/** Inferred type from the Zod schema */
|
|
type GeneratedTestCasesOutput = z.infer<typeof generatedTestCasesSchema>;
|
|
|
|
/** Parse and validate LLM output using the Zod schema */
|
|
function parseTestCasesOutput(value: unknown): GeneratedTestCasesOutput {
|
|
const parsed = generatedTestCasesSchema.safeParse(value);
|
|
if (!parsed.success) {
|
|
throw new Error(`Invalid LLM output: ${parsed.error.message}`);
|
|
}
|
|
return parsed.data;
|
|
}
|
|
|
|
/**
|
|
* System prompt for test case generation.
|
|
*/
|
|
const systemPrompt = `You are an expert at generating diverse test cases for an n8n workflow builder AI. Create test cases that cover various real-world scenarios and complexity levels.
|
|
|
|
## Test Case Requirements:
|
|
|
|
1. **Simple Test Cases**: Single operation workflows
|
|
- API calls
|
|
- Data transformations
|
|
- File operations
|
|
- Basic integrations
|
|
|
|
2. **Medium Test Cases**: Multi-step workflows with logic
|
|
- Conditional logic (IF nodes)
|
|
- Data filtering and transformation
|
|
- Multiple API integrations
|
|
- Error handling
|
|
|
|
3. **Complex Test Cases**: Advanced workflows
|
|
- Parallel execution branches
|
|
- Complex error handling and retry logic
|
|
- Multiple integrations with data synchronization
|
|
- Webhooks and event-driven flows
|
|
|
|
## Guidelines:
|
|
- Create realistic business scenarios
|
|
- Include specific requirements that can be evaluated
|
|
- Vary the domains (e-commerce, HR, marketing, DevOps, etc.)
|
|
- Include both common and edge-case scenarios
|
|
- Make prompts clear and unambiguous
|
|
- Specify expected node types when possible
|
|
|
|
## Output Format:
|
|
Each test case should have:
|
|
- Unique ID (e.g., "test_001")
|
|
- Descriptive name
|
|
- Brief description
|
|
- Clear prompt that a user would give`;
|
|
|
|
/**
|
|
* Get default focus based on complexity option.
|
|
*/
|
|
function getFocus(options?: TestCaseGeneratorOptions): string {
|
|
const complexity = options?.complexity ?? 'balanced';
|
|
|
|
if (options?.focus) {
|
|
return options.focus;
|
|
}
|
|
|
|
switch (complexity) {
|
|
case 'simple':
|
|
return 'simple, single-operation workflows like basic API calls, data transformations, and file operations';
|
|
case 'complex':
|
|
return 'complex, multi-step workflows with parallel execution, error handling, and multiple integrations';
|
|
case 'balanced':
|
|
default:
|
|
return 'balanced mix of API integrations, data processing, and automation scenarios';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the human message content.
|
|
*/
|
|
function buildHumanMessage(count: number, focus: string): string {
|
|
return `Generate ${count} diverse test cases for workflow generation evaluation.
|
|
|
|
Focus on:
|
|
${focus}
|
|
|
|
Ensure a good mix of complexity levels and use cases.`;
|
|
}
|
|
|
|
/**
|
|
* Create a test case generator that uses LLM to generate test cases.
|
|
*
|
|
* @param llm - Language model to use for generation
|
|
* @param options - Generation options
|
|
* @returns A test case generator
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const generator = createTestCaseGenerator(llm, { count: 20 });
|
|
* const testCases = await generator.generate();
|
|
* ```
|
|
*/
|
|
export function createTestCaseGenerator(
|
|
llm: BaseChatModel,
|
|
options?: TestCaseGeneratorOptions,
|
|
): TestCaseGenerator {
|
|
const count = options?.count ?? 10;
|
|
const focus = getFocus(options);
|
|
|
|
// Create LLM with structured output
|
|
const llmWithStructuredOutput = llm.withStructuredOutput(generatedTestCasesSchema);
|
|
|
|
return {
|
|
async generate(): Promise<GeneratedTestCase[]> {
|
|
const humanMessage = buildHumanMessage(count, focus);
|
|
|
|
const rawResult = await llmWithStructuredOutput.invoke([
|
|
new SystemMessage(systemPrompt),
|
|
new HumanMessage(humanMessage),
|
|
]);
|
|
|
|
// Validate and parse the LLM output using Zod
|
|
const result = parseTestCasesOutput(rawResult);
|
|
|
|
return result.testCases;
|
|
},
|
|
};
|
|
}
|