Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
139 lines
No EOL
5.4 KiB
JavaScript
139 lines
No EOL
5.4 KiB
JavaScript
/**
|
|
* Integration Tests for Labs Node Types System
|
|
* @see ADR-043: Laboratories Feature
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import { LabsService } from '../index.js';
|
|
|
|
describe('Labs Node Types Integration', () => {
|
|
it('should have complete node type system', () => {
|
|
// Test that all required node types exist
|
|
const types = LabsService.getNodeTypes();
|
|
expect(types).toHaveLength(8);
|
|
|
|
// Test each category has at least one node type
|
|
const categories = types.map(t => t.category);
|
|
expect(categories).toContain('basic');
|
|
expect(categories).toContain('ai');
|
|
expect(categories).toContain('media');
|
|
expect(categories).toContain('io');
|
|
expect(categories).toContain('dev');
|
|
});
|
|
|
|
it('should create and validate nodes for each type', () => {
|
|
const nodeTypes = ['text', 'ai_agent', 'image', 'file', 'input', 'output', 'code', 'note'];
|
|
|
|
for (const typeKey of nodeTypes) {
|
|
// Create node with defaults
|
|
const nodeData = LabsService.createNodeWithDefaults(typeKey, {
|
|
title: `Test ${typeKey} Node`
|
|
});
|
|
|
|
// Validate the created node
|
|
const validation = LabsService.validateNode(nodeData);
|
|
expect(validation.valid).toBe(true);
|
|
expect(validation.errors).toHaveLength(0);
|
|
|
|
// Check required fields
|
|
expect(nodeData.type_key).toBe(typeKey);
|
|
expect(nodeData.title).toBe(`Test ${typeKey} Node`);
|
|
expect(nodeData.node_id).toBeDefined();
|
|
expect(nodeData.width).toBeGreaterThan(0);
|
|
expect(nodeData.height).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('should execute all node types without errors', async () => {
|
|
// Note: AI agent node requires a real agent in DB, so we test it separately
|
|
const testNodes = [
|
|
{ type_key: 'text', content: 'Test text', config: {} },
|
|
{ type_key: 'image', config: { src: 'test.jpg' } },
|
|
{ type_key: 'file', config: { filename: 'test.txt' } },
|
|
{ type_key: 'input', config: { inputType: 'text' } },
|
|
{ type_key: 'output', content: 'Test output', config: {} },
|
|
{ type_key: 'code', content: 'console.log("test");', config: {} },
|
|
{ type_key: 'note', content: 'Test note', config: {} }
|
|
];
|
|
|
|
for (const node of testNodes) {
|
|
const result = await LabsService.executeNode(node);
|
|
expect(result.success).toBe(true);
|
|
expect(result.output).toBeDefined();
|
|
expect(result.type).toBe(node.type_key);
|
|
}
|
|
});
|
|
|
|
it('should handle AI agent node execution (error in test env)', async () => {
|
|
const aiNode = { type_key: 'ai_agent', config: { ai_agent_id: 123 } };
|
|
const result = await LabsService.executeNode(aiNode);
|
|
|
|
// In test environment, either agent doesn't exist or DB query fails
|
|
expect(result.success).toBe(false);
|
|
expect(result.type).toBe('ai_agent');
|
|
// Error can be "AI agent 123 not found" or "AI agent execution failed"
|
|
expect(result.error).toMatch(/AI agent/);
|
|
});
|
|
|
|
it('should handle edge cases gracefully', async () => {
|
|
// Test AI agent without agent ID
|
|
const aiNodeWithoutAgent = {
|
|
type_key: 'ai_agent',
|
|
config: {}
|
|
};
|
|
|
|
const result = await LabsService.executeNode(aiNodeWithoutAgent);
|
|
expect(result.success).toBe(false);
|
|
// Error can be either "No AI agent selected" or "No API key configured for provider: openai"
|
|
expect(result.error).toMatch(/No AI agent selected|No API key configured/);
|
|
|
|
// Test image without source
|
|
const imageNodeWithoutSrc = {
|
|
type_key: 'image',
|
|
config: {}
|
|
};
|
|
|
|
const imageResult = await LabsService.executeNode(imageNodeWithoutSrc);
|
|
expect(imageResult.success).toBe(false);
|
|
expect(imageResult.error).toBe('No image source provided');
|
|
|
|
// Test file without filename
|
|
const fileNodeWithoutName = {
|
|
type_key: 'file',
|
|
config: {}
|
|
};
|
|
|
|
const fileResult = await LabsService.executeNode(fileNodeWithoutName);
|
|
expect(fileResult.success).toBe(false);
|
|
expect(fileResult.error).toBe('No file specified');
|
|
});
|
|
|
|
it('should validate configurations correctly', () => {
|
|
// Test valid configurations
|
|
const validConfigs = [
|
|
{ type: 'text', config: { fontSize: 16, fontWeight: 'bold' } },
|
|
{ type: 'ai_agent', config: { temperature: 0.7, max_tokens: 1000 } },
|
|
{ type: 'code', config: { language: 'javascript', theme: 'dark' } },
|
|
{ type: 'note', config: { color: 'yellow', priority: 'high' } }
|
|
];
|
|
|
|
for (const { type, config } of validConfigs) {
|
|
const result = LabsService.validateNodeConfig(type, config);
|
|
expect(result.valid).toBe(true);
|
|
expect(result.errors).toHaveLength(0);
|
|
}
|
|
|
|
// Test invalid configurations
|
|
const invalidConfigs = [
|
|
{ type: 'text', config: { fontSize: 100 }, expectedError: 'fontSize must be a number between 8 and 72' },
|
|
{ type: 'ai_agent', config: { temperature: 5 }, expectedError: 'temperature must be a number between 0 and 2' },
|
|
{ type: 'code', config: { language: 'unknown' }, expectedError: 'language must be one of:' },
|
|
{ type: 'note', config: { color: 'purple' }, expectedError: 'color must be one of: yellow, blue, green, pink, orange' }
|
|
];
|
|
|
|
for (const { type, config, expectedError } of invalidConfigs) {
|
|
const result = LabsService.validateNodeConfig(type, config);
|
|
expect(result.valid).toBe(false);
|
|
expect(result.errors.some(error => error.includes(expectedError))).toBe(true);
|
|
}
|
|
});
|
|
}); |