aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
303 lines (247 loc) • 9.97 kB
JavaScript
/**
* @fileoverview Unit tests for WorkflowRegistry
* @story WIS-2 - Workflow Registry Enhancement
*/
;
const path = require('path');
const {
WorkflowRegistry,
createWorkflowRegistry,
DEFAULT_CACHE_TTL,
} = require('../registry/workflow-registry');
describe('WorkflowRegistry', () => {
let registry;
beforeEach(() => {
registry = createWorkflowRegistry();
});
afterEach(() => {
registry.invalidateCache();
});
describe('constructor', () => {
it('should create registry with default options', () => {
expect(registry.cacheTTL).toBe(DEFAULT_CACHE_TTL);
expect(registry.cache).toBeNull();
});
it('should accept custom cache TTL', () => {
const customRegistry = createWorkflowRegistry({ cacheTTL: 1000 });
expect(customRegistry.cacheTTL).toBe(1000);
});
it('should accept custom patterns path', () => {
const customPath = '/custom/path.yaml';
const customRegistry = createWorkflowRegistry({ patternsPath: customPath });
expect(customRegistry.patternsPath).toBe(customPath);
});
});
describe('loadWorkflows()', () => {
it('should load workflow patterns from file', () => {
const workflows = registry.loadWorkflows();
expect(workflows).toBeDefined();
expect(typeof workflows).toBe('object');
});
it('should return 10 workflows', () => {
const workflows = registry.loadWorkflows();
const names = Object.keys(workflows);
expect(names.length).toBe(10);
});
it('should include story_development workflow', () => {
const workflows = registry.loadWorkflows();
expect(workflows.story_development).toBeDefined();
});
it('should include epic_creation workflow', () => {
const workflows = registry.loadWorkflows();
expect(workflows.epic_creation).toBeDefined();
});
it('should cache loaded workflows', () => {
const workflows1 = registry.loadWorkflows();
const workflows2 = registry.loadWorkflows();
expect(workflows1).toBe(workflows2);
});
it('should throw error for invalid file path', () => {
const badRegistry = createWorkflowRegistry({
patternsPath: '/nonexistent/path.yaml',
});
expect(() => badRegistry.loadWorkflows()).toThrow('Workflow patterns file not found');
});
});
describe('isCacheValid()', () => {
it('should return false when cache is null', () => {
expect(registry.isCacheValid()).toBe(false);
});
it('should return true after loading workflows', () => {
registry.loadWorkflows();
expect(registry.isCacheValid()).toBe(true);
});
it('should return false after invalidation', () => {
registry.loadWorkflows();
registry.invalidateCache();
expect(registry.isCacheValid()).toBe(false);
});
});
describe('getWorkflowNames()', () => {
it('should return array of workflow names', () => {
const names = registry.getWorkflowNames();
expect(Array.isArray(names)).toBe(true);
expect(names.length).toBe(10);
});
it('should include expected workflows', () => {
const names = registry.getWorkflowNames();
expect(names).toContain('story_development');
expect(names).toContain('epic_creation');
expect(names).toContain('backlog_management');
expect(names).toContain('architecture_review');
expect(names).toContain('git_workflow');
expect(names).toContain('database_workflow');
expect(names).toContain('code_quality_workflow');
expect(names).toContain('documentation_workflow');
expect(names).toContain('ux_workflow');
expect(names).toContain('research_workflow');
});
});
describe('getWorkflow()', () => {
it('should return workflow by name', () => {
const workflow = registry.getWorkflow('epic_creation');
expect(workflow).toBeDefined();
expect(workflow.description).toBeDefined();
});
it('should return null for unknown workflow', () => {
const workflow = registry.getWorkflow('nonexistent');
expect(workflow).toBeNull();
});
it('should include transitions in workflow', () => {
const workflow = registry.getWorkflow('epic_creation');
expect(workflow.transitions).toBeDefined();
expect(typeof workflow.transitions).toBe('object');
});
});
describe('matchWorkflow()', () => {
it('should match workflow from command history', () => {
const commands = ['create-epic', 'create-story'];
const match = registry.matchWorkflow(commands);
expect(match).not.toBeNull();
expect(match.name).toBe('epic_creation');
});
it('should return best matching workflow', () => {
const commands = ['validate-story-draft', 'develop', 'review-qa'];
const match = registry.matchWorkflow(commands);
expect(match).not.toBeNull();
expect(match.name).toBe('story_development');
});
it('should return null for empty commands', () => {
const match = registry.matchWorkflow([]);
expect(match).toBeNull();
});
it('should return null for unmatched commands', () => {
const match = registry.matchWorkflow(['random-cmd', 'another-random']);
expect(match).toBeNull();
});
it('should include score in match result', () => {
const match = registry.matchWorkflow(['create-epic', 'create-story']);
expect(match.score).toBeGreaterThanOrEqual(2);
});
it('should include matched commands', () => {
const match = registry.matchWorkflow(['create-epic', 'create-story']);
expect(match.matchedCommands).toBeDefined();
expect(Array.isArray(match.matchedCommands)).toBe(true);
});
});
describe('getTransitions()', () => {
it('should return transitions for valid state', () => {
const transition = registry.getTransitions('epic_creation', 'epic_drafted');
expect(transition).not.toBeNull();
expect(transition.trigger).toBeDefined();
expect(transition.confidence).toBeDefined();
expect(transition.next_steps).toBeDefined();
});
it('should return null for unknown state', () => {
const transition = registry.getTransitions('epic_creation', 'unknown_state');
expect(transition).toBeNull();
});
it('should return null for unknown workflow', () => {
const transition = registry.getTransitions('unknown_workflow', 'any_state');
expect(transition).toBeNull();
});
it('should return empty array for workflow without transitions', () => {
const result = registry.getAllTransitions('nonexistent');
expect(result).toEqual({});
});
});
describe('getNextSteps()', () => {
it('should return sorted next steps', () => {
const steps = registry.getNextSteps('epic_creation', 'epic_drafted');
expect(Array.isArray(steps)).toBe(true);
expect(steps.length).toBeGreaterThan(0);
expect(steps[0].priority).toBeLessThanOrEqual(steps[1]?.priority || 99);
});
it('should include command and description', () => {
const steps = registry.getNextSteps('epic_creation', 'epic_drafted');
expect(steps[0].command).toBeDefined();
expect(steps[0].description).toBeDefined();
});
it('should return empty array for unknown state', () => {
const steps = registry.getNextSteps('epic_creation', 'unknown_state');
expect(steps).toEqual([]);
});
});
describe('findCurrentState()', () => {
it('should find state from command', () => {
const state = registry.findCurrentState('epic_creation', 'create-epic completed');
expect(state).toBe('epic_drafted');
});
it('should find state without "completed" suffix', () => {
const state = registry.findCurrentState('epic_creation', 'create-epic');
expect(state).toBe('epic_drafted');
});
it('should return null for unknown command', () => {
const state = registry.findCurrentState('epic_creation', 'random-command');
expect(state).toBeNull();
});
it('should return null for unknown workflow', () => {
const state = registry.findCurrentState('unknown', 'create-epic');
expect(state).toBeNull();
});
});
describe('getWorkflowsByAgent()', () => {
it('should return workflows for agent', () => {
const workflows = registry.getWorkflowsByAgent('@po');
expect(Array.isArray(workflows)).toBe(true);
expect(workflows.length).toBeGreaterThan(0);
});
it('should handle agent without @ prefix', () => {
const workflows = registry.getWorkflowsByAgent('po');
expect(workflows.length).toBeGreaterThan(0);
});
it('should return empty array for unknown agent', () => {
const workflows = registry.getWorkflowsByAgent('@unknown');
expect(workflows).toEqual([]);
});
});
describe('getStats()', () => {
it('should return registry statistics', () => {
const stats = registry.getStats();
expect(stats.totalWorkflows).toBe(10);
expect(stats.workflowsWithTransitions).toBe(10);
expect(stats.totalTransitions).toBeGreaterThan(0);
});
it('should report cache status', () => {
registry.loadWorkflows();
const stats = registry.getStats();
expect(stats.cacheValid).toBe(true);
expect(stats.cacheAge).toBeGreaterThanOrEqual(0);
});
});
describe('normalizeCommand()', () => {
it('should normalize command strings', () => {
expect(registry.normalizeCommand('CREATE-EPIC')).toBe('create-epic');
expect(registry.normalizeCommand('*create-epic')).toBe('create-epic');
expect(registry.normalizeCommand('create-epic completed')).toBe('create-epic');
});
it('should handle null input', () => {
expect(registry.normalizeCommand(null)).toBe('');
});
});
describe('DEFAULT_CACHE_TTL', () => {
it('should be 5 minutes', () => {
expect(DEFAULT_CACHE_TTL).toBe(5 * 60 * 1000);
});
});
});