hybrid-dag-kubernetes.test.ts•19.5 kB
/**
* End-to-End Integration Test for Hybrid DAG Architecture with Kubernetes Pattern
*
* This test validates the complete workflow:
* 1. Loading validated patterns from YAML
* 2. Converting patterns to executable DAG tasks
* 3. Building and validating DAG structure
* 4. Verifying dependency resolution and task ordering
*/
import { describe, it, expect, beforeAll } from '@jest/globals';
import { PatternLoader } from '../../src/utils/pattern-loader.js';
import { PatternToDAGConverter } from '../../src/utils/pattern-to-dag-converter.js';
import { DAGExecutor } from '../../src/utils/dag-executor.js';
describe('Hybrid DAG Architecture - Kubernetes E2E Test', () => {
let patternLoader: PatternLoader;
let patternConverter: PatternToDAGConverter;
let dagExecutor: DAGExecutor;
beforeAll(() => {
patternLoader = new PatternLoader();
patternConverter = new PatternToDAGConverter();
dagExecutor = new DAGExecutor(3); // Max 3 parallel tasks for testing
});
describe('Pattern Loading and Validation', () => {
it('should load Kubernetes pattern from YAML', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
expect(pattern).toBeDefined();
expect(pattern.id).toBe('kubernetes-v1');
expect(pattern.name).toBe('Kubernetes');
expect(pattern.dependencies).toBeDefined();
expect(pattern.deploymentPhases).toBeDefined();
expect(pattern.validationChecks).toBeDefined();
});
it('should validate Kubernetes pattern structure', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
// Validate dependencies
expect(pattern.dependencies.length).toBeGreaterThan(0);
const kubectlDep = pattern.dependencies.find(d => d.name === 'kubectl');
expect(kubectlDep).toBeDefined();
expect(kubectlDep?.required).toBe(true);
expect(kubectlDep?.installCommand).toBeDefined();
expect(kubectlDep?.verificationCommand).toBeDefined();
// Validate deployment phases
expect(pattern.deploymentPhases.length).toBe(4);
expect(pattern.deploymentPhases[0]?.name).toBe('Prerequisites Validation');
expect(pattern.deploymentPhases[1]?.name).toBe('Namespace Setup');
expect(pattern.deploymentPhases[2]?.name).toBe('Deploy Application Resources');
expect(pattern.deploymentPhases[3]?.name).toBe('Verify Deployment');
// Validate validation checks
expect(pattern.validationChecks.length).toBe(3);
const clusterCheck = pattern.validationChecks.find(c => c.id === 'cluster-connection');
expect(clusterCheck).toBeDefined();
expect(clusterCheck?.severity).toBe('critical');
});
it('should have proper authoritative sources', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
expect(pattern.authoritativeSources).toBeDefined();
expect(pattern.authoritativeSources.length).toBeGreaterThan(0);
// Should have official Kubernetes documentation
const officialDocs = pattern.authoritativeSources.find(s =>
s.url.includes('kubernetes.io/docs')
);
expect(officialDocs).toBeDefined();
expect(officialDocs?.priority).toBe(10);
expect(officialDocs?.requiredForDeployment).toBe(true);
});
});
describe('Pattern to DAG Conversion', () => {
it('should convert Kubernetes pattern to DAG tasks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
expect(dagTasks).toBeDefined();
expect(dagTasks.length).toBeGreaterThan(0);
// Should have dependency install/verify tasks
const installTasks = dagTasks.filter(t => t.id.includes('install'));
expect(installTasks.length).toBeGreaterThan(0);
// Should have deployment phase tasks
const phaseTasks = dagTasks.filter(t => t.id.includes('phase'));
expect(phaseTasks.length).toBeGreaterThan(0);
// Should have validation tasks
const validationTasks = dagTasks.filter(t => t.id.includes('validate'));
expect(validationTasks.length).toBeGreaterThan(0);
});
it('should create tasks with proper dependency chains', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find kubectl install and verify tasks
const kubectlInstall = dagTasks.find(t => t.id === 'kubernetes-install-kubectl');
const kubectlVerify = dagTasks.find(t => t.id === 'kubernetes-verify-kubectl');
expect(kubectlInstall).toBeDefined();
expect(kubectlVerify).toBeDefined();
// Verify task should depend on install task
expect(kubectlVerify?.dependsOn).toContain('kubernetes-install-kubectl');
});
it('should create tasks with infrastructure category and proper severity', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// All tasks should be infrastructure category
expect(dagTasks.every(t => t.category === 'infrastructure')).toBe(true);
// Critical tasks (first phase, required dependencies)
const criticalTasks = dagTasks.filter(t => t.severity === 'critical');
expect(criticalTasks.length).toBeGreaterThan(0);
// Dependency tasks should be critical
const kubectlInstall = dagTasks.find(t => t.id === 'kubernetes-install-kubectl');
expect(kubectlInstall?.severity).toBe('critical');
});
it('should map deployment phase prerequisites to task dependencies', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find phase 2 tasks (Namespace Setup) - should depend on phase 1 (Prerequisites Validation)
const phase2Tasks = dagTasks.filter(t => t.id.includes('phase-2'));
const phase1Tasks = dagTasks.filter(t => t.id.includes('phase-1'));
if (phase2Tasks.length > 0 && phase1Tasks.length > 0) {
const phase2Task = phase2Tasks[0];
const phase1TaskIds = phase1Tasks.map(t => t.id);
// Phase 2 should depend on at least one phase 1 task
const hasPhase1Dependency = phase2Task?.dependsOn.some(dep => phase1TaskIds.includes(dep));
expect(hasPhase1Dependency).toBe(true);
}
});
it('should create validation check tasks that depend on deployment tasks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find validation tasks
const validationTasks = dagTasks.filter(t => t.id.includes('validate'));
if (validationTasks.length > 0) {
const validationTask = validationTasks[0];
// Validation tasks should depend on deployment/phase tasks
expect(validationTask?.dependsOn.length).toBeGreaterThan(0);
// Should not depend on other validation tasks
const dependsOnValidation = validationTask?.dependsOn.some(dep => dep.includes('validate'));
expect(dependsOnValidation).toBe(false);
}
});
it('should generate correct task count based on pattern structure', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Expected task count:
// - 2 tasks per required dependency (install + verify) = kubectl + kubeconfig = 4 tasks
// - Commands from phase 1, 2, 3, 4 = multiple tasks
// - 3 validation checks = 3 tasks
// Total should be significant
expect(dagTasks.length).toBeGreaterThanOrEqual(10);
// Log task breakdown for debugging
console.log('Task breakdown:');
console.log(` Total tasks: ${dagTasks.length}`);
console.log(` Install tasks: ${dagTasks.filter(t => t.id.includes('install')).length}`);
console.log(` Verify tasks: ${dagTasks.filter(t => t.id.includes('verify')).length}`);
console.log(` Phase tasks: ${dagTasks.filter(t => t.id.includes('phase')).length}`);
console.log(` Validation tasks: ${dagTasks.filter(t => t.id.includes('validate')).length}`);
});
});
describe('DAG Structure Validation', () => {
it('should create a valid DAG without cycles', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// DAGExecutor.validateDAG will throw if there are cycles
expect(() => {
// Access private method through type casting for testing
const executor = dagExecutor as any;
executor.validateDAG(dagTasks);
}).not.toThrow();
});
it('should have no duplicate task IDs', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
const taskIds = dagTasks.map(t => t.id);
const uniqueIds = new Set(taskIds);
expect(taskIds.length).toBe(uniqueIds.size);
});
it('should have all dependencies exist as tasks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
const taskIds = new Set(dagTasks.map(t => t.id));
for (const task of dagTasks) {
for (const depId of task.dependsOn) {
expect(taskIds.has(depId)).toBe(true);
}
}
});
it('should have proper topological order', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Build dependency graph and get topological sort
const executor = dagExecutor as any;
const graph = executor.buildDependencyGraph(dagTasks);
const layers = executor.topologicalSort(graph);
expect(layers.length).toBeGreaterThan(0);
// First layer should have no dependencies or only dependencies within the layer
const firstLayer = layers[0];
if (firstLayer) {
for (const task of firstLayer) {
const allDepsInFirstLayer = task.dependsOn.every(depId =>
firstLayer.some(t => t.id === depId)
);
expect(task.dependsOn.length === 0 || allDepsInFirstLayer).toBe(true);
}
}
// Each subsequent layer should only depend on previous layers
for (let i = 1; i < layers.length; i++) {
const layer = layers[i];
const previousLayerIds = new Set(layers.slice(0, i).flatMap(l => l?.map(t => t.id) || []));
if (layer) {
for (const task of layer) {
const allDepsInPreviousLayers = task.dependsOn.every(
depId => previousLayerIds.has(depId) || layer.some(t => t.id === depId)
);
expect(allDepsInPreviousLayers).toBe(true);
}
}
}
});
it('should identify critical path tasks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Critical path should include:
// 1. Required dependency installations
// 2. Phase 1 (Prerequisites Validation)
// 3. Critical validation checks
const criticalTasks = dagTasks.filter(t => t.severity === 'critical');
expect(criticalTasks.length).toBeGreaterThan(0);
// Should include kubectl installation
const kubectlInstall = criticalTasks.find(t => t.id.includes('kubectl'));
expect(kubectlInstall).toBeDefined();
});
it('should properly layer tasks for parallel execution', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
const executor = dagExecutor as any;
const graph = executor.buildDependencyGraph(dagTasks);
const layers = executor.topologicalSort(graph);
// Should have multiple layers
expect(layers.length).toBeGreaterThan(1);
// Log layer information
console.log('\nDAG Layer Structure:');
for (let i = 0; i < layers.length; i++) {
const layer = layers[i];
if (layer) {
console.log(` Layer ${i + 1}: ${layer.length} tasks`);
console.log(` Tasks: ${layer.map(t => t.name).join(', ')}`);
}
}
});
});
describe('Validation Check Integration', () => {
it('should create validation functions from validation checks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find validation tasks
const validationTasks = dagTasks.filter(t => t.validationCheck);
expect(validationTasks.length).toBeGreaterThan(0);
// Each validation task should have a validation function
for (const task of validationTasks) {
expect(task.validationCheck).toBeDefined();
expect(typeof task.validationCheck).toBe('function');
}
});
it('should validate task output using custom validation functions', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find a validation task
const validationTask = dagTasks.find(t => t.validationCheck);
if (validationTask?.validationCheck) {
// Test validation function with success output
const successOutput = 'Pods are running and ready';
expect(validationTask.validationCheck(successOutput)).toBe(true);
// Test validation function with failure output
const failureOutput = 'Error: pod not found';
expect(validationTask.validationCheck(failureOutput)).toBe(false);
}
});
it('should detect failure keywords in validation output', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
const validationTask = dagTasks.find(t => t.validationCheck);
if (validationTask?.validationCheck) {
// Test various failure scenarios
const failureOutputs = [
'error: connection refused',
'failed to connect',
'not found: cluster',
'cannot access cluster',
'unable to authenticate',
'access denied',
];
for (const output of failureOutputs) {
expect(validationTask.validationCheck(output)).toBe(false);
}
}
});
it('should detect success indicators in validation output', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
// Find deployment/ready validation task
const deploymentTask = dagTasks.find(
t => t.id.includes('deployment-ready') || t.id.includes('ready')
);
if (deploymentTask?.validationCheck) {
// Test success outputs
expect(deploymentTask.validationCheck('deployment is ready')).toBe(true);
expect(deploymentTask.validationCheck('pods are running')).toBe(true);
}
});
});
describe('Task Configuration Verification', () => {
it('should have proper command structure for all tasks', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
for (const task of dagTasks) {
// Task should have either a command or be marked as skippable
if (!task.canFailSafely) {
expect(task.command || task.commandArgs).toBeDefined();
}
// If command exists, it should be a string
if (task.command) {
expect(typeof task.command).toBe('string');
expect(task.command.length).toBeGreaterThan(0);
}
// Command args should be an array if present
if (task.commandArgs) {
expect(Array.isArray(task.commandArgs)).toBe(true);
}
}
});
it('should have reasonable timeout values', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
for (const task of dagTasks) {
if (task.timeout) {
// Timeout should be between 1 second and 10 minutes
expect(task.timeout).toBeGreaterThanOrEqual(1000);
expect(task.timeout).toBeLessThanOrEqual(600000);
}
}
});
it('should have proper task naming conventions', async () => {
const pattern = await patternLoader.loadPattern('kubernetes');
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
for (const task of dagTasks) {
// ID should start with platform name
expect(task.id).toMatch(/^kubernetes-/);
// Name should be descriptive
expect(task.name.length).toBeGreaterThan(0);
// Description should be provided
expect(task.description.length).toBeGreaterThan(0);
}
});
});
describe('Complete Workflow Integration', () => {
it('should demonstrate complete pattern-to-DAG workflow', async () => {
// This test demonstrates the complete workflow:
// 1. Load pattern
// 2. Convert to DAG
// 3. Validate structure
// 4. Verify execution order
// Step 1: Load pattern
const pattern = await patternLoader.loadPattern('kubernetes');
expect(pattern).toBeDefined();
expect(pattern.id).toBe('kubernetes-v1');
// Step 2: Convert to DAG
const dagTasks = patternConverter.buildInfrastructureTasksFromPattern(pattern, 'kubernetes');
expect(dagTasks.length).toBeGreaterThan(0);
// Step 3: Validate DAG structure
const executor = dagExecutor as any;
expect(() => executor.validateDAG(dagTasks)).not.toThrow();
// Step 4: Verify execution order
const graph = executor.buildDependencyGraph(dagTasks);
const layers = executor.topologicalSort(graph);
expect(layers.length).toBeGreaterThan(0);
// Log workflow summary
console.log('\n=== Kubernetes Pattern Workflow Summary ===');
console.log(`Pattern: ${pattern.name} (${pattern.id})`);
console.log(`Generated Tasks: ${dagTasks.length}`);
console.log(`Execution Layers: ${layers.length}`);
console.log(`Max Parallelism: 3 tasks per layer`);
console.log(`Critical Tasks: ${dagTasks.filter(t => t.severity === 'critical').length}`);
console.log('==========================================\n');
}, 30000); // 30 second timeout for complete workflow
});
});