UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

217 lines (172 loc) 8.64 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { MethodologyManager } from '../methodology-manager.js'; import { ConfigManager } from '../../../config/config-manager.js'; import { AGILE_TOOLS, KANBAN_TOOLS, COMMON_TOOLS } from '../types.js'; vi.mock('../../../config/config-manager.js'); describe('MethodologyManager', () => { let methodologyManager: MethodologyManager; let mockConfigManager: vi.Mocked<ConfigManager>; beforeEach(async () => { mockConfigManager = new ConfigManager() as vi.Mocked<ConfigManager>; mockConfigManager.getStorageManager = vi.fn().mockReturnValue({ loadData: vi.fn().mockResolvedValue(null), saveData: vi.fn().mockResolvedValue(undefined), ensureStorageDirectories: vi.fn().mockResolvedValue(undefined) }); methodologyManager = new MethodologyManager(mockConfigManager); await methodologyManager.init(); }); describe('setMethodology', () => { it('should set agile methodology with correct tools', async () => { const methodology = await methodologyManager.setMethodology('agile', 'user123'); expect(methodology.type).toBe('agile'); expect(methodology.allowedTools).toContain(...COMMON_TOOLS); expect(methodology.allowedTools).toContain(...AGILE_TOOLS); expect(methodology.disallowedTools).toEqual(KANBAN_TOOLS); }); it('should set kanban methodology with correct tools', async () => { const methodology = await methodologyManager.setMethodology('kanban', 'user123'); expect(methodology.type).toBe('kanban'); expect(methodology.allowedTools).toContain(...COMMON_TOOLS); expect(methodology.allowedTools).toContain(...KANBAN_TOOLS); expect(methodology.disallowedTools).toEqual(AGILE_TOOLS); }); it('should record migration when switching methodologies', async () => { // Set initial methodology await methodologyManager.setMethodology('agile', 'user123'); // Switch to kanban const methodology = await methodologyManager.setMethodology('kanban', 'user123'); expect(methodology.migrationHistory).toHaveLength(1); const migration = methodology.migrationHistory[0]; expect(migration.fromType).toBe('agile'); expect(migration.toType).toBe('kanban'); expect(migration.migratedBy).toBe('user123'); expect(migration.dataLost).toContain('Sprint information will be lost'); }); it('should emit methodology-changed event', async () => { let eventEmitted = false; methodologyManager.once('methodology-changed', (methodology) => { eventEmitted = true; expect(methodology.type).toBe('agile'); }); await methodologyManager.setMethodology('agile', 'user123'); expect(eventEmitted).toBe(true); }); }); describe('lockMethodology', () => { it('should lock methodology preventing changes', async () => { await methodologyManager.setMethodology('agile', 'user123'); await methodologyManager.lockMethodology('admin'); // Try to change methodology await expect( methodologyManager.setMethodology('kanban', 'user456') ).rejects.toThrow('Methodology is locked'); }); it('should record who locked the methodology', async () => { await methodologyManager.setMethodology('agile', 'user123'); await methodologyManager.lockMethodology('admin'); const methodology = methodologyManager.getCurrentMethodology(); expect(methodology?.lockedBy).toBe('admin'); expect(methodology?.lockedAt).toBeDefined(); }); it('should not allow locking twice', async () => { await methodologyManager.setMethodology('agile', 'user123'); await methodologyManager.lockMethodology('admin'); await expect( methodologyManager.lockMethodology('admin2') ).rejects.toThrow('Methodology is already locked'); }); it('should emit methodology-locked event', async () => { await methodologyManager.setMethodology('agile', 'user123'); let eventEmitted = false; methodologyManager.once('methodology-locked', (methodology) => { eventEmitted = true; expect(methodology.type).toBe('agile'); }); await methodologyManager.lockMethodology('admin'); expect(eventEmitted).toBe(true); }); }); describe('isToolAllowed', () => { it('should allow common tools in both methodologies', async () => { await methodologyManager.setMethodology('agile', 'user123'); for (const tool of COMMON_TOOLS) { expect(methodologyManager.isToolAllowed(tool)).toBe(true); } }); it('should allow agile tools only in agile methodology', async () => { await methodologyManager.setMethodology('agile', 'user123'); for (const tool of AGILE_TOOLS) { expect(methodologyManager.isToolAllowed(tool)).toBe(true); } for (const tool of KANBAN_TOOLS) { expect(methodologyManager.isToolAllowed(tool)).toBe(false); } }); it('should allow kanban tools only in kanban methodology', async () => { await methodologyManager.setMethodology('kanban', 'user123'); for (const tool of KANBAN_TOOLS) { expect(methodologyManager.isToolAllowed(tool)).toBe(true); } for (const tool of AGILE_TOOLS) { expect(methodologyManager.isToolAllowed(tool)).toBe(false); } }); it('should allow all tools when no methodology is set', () => { expect(methodologyManager.isToolAllowed('any-tool')).toBe(true); }); }); describe('warnAboutConflict', () => { it('should emit warning for conflicting tool usage', async () => { await methodologyManager.setMethodology('agile', 'user123'); let warningEmitted = false; methodologyManager.once('methodology-conflict-warning', (warning) => { warningEmitted = true; expect(warning.toolName).toBe('create_board'); expect(warning.currentMethodology).toBe('agile'); }); const shouldWarn = await methodologyManager.warnAboutConflict('create_board'); expect(shouldWarn).toBe(true); expect(warningEmitted).toBe(true); }); it('should throttle warnings to once per hour', async () => { await methodologyManager.setMethodology('agile', 'user123'); const firstWarn = await methodologyManager.warnAboutConflict('create_board'); expect(firstWarn).toBe(true); const secondWarn = await methodologyManager.warnAboutConflict('create_board'); expect(secondWarn).toBe(false); }); }); describe('suggestMigrationPath', () => { it('should suggest steps for agile to kanban migration', async () => { const suggestions = await methodologyManager.suggestMigrationPath('agile', 'kanban'); expect(suggestions).toContain('1. Complete or cancel active sprints'); expect(suggestions).toContain('2. Convert stories to tasks'); expect(suggestions).toContain('3. Set up WIP limits for columns'); }); it('should suggest steps for kanban to agile migration', async () => { const suggestions = await methodologyManager.suggestMigrationPath('kanban', 'agile'); expect(suggestions).toContain('1. Group related tasks into stories'); expect(suggestions).toContain('2. Estimate story points for all items'); expect(suggestions).toContain('3. Create initial sprint backlog'); }); }); describe('Default configurations', () => { it('should set appropriate defaults for agile', async () => { const methodology = await methodologyManager.setMethodology('agile', 'user123'); expect(methodology.configuration.sprintDuration).toBe(14); expect(methodology.configuration.velocityTracking).toBe(true); expect(methodology.configuration.storyPointScale).toEqual([1, 2, 3, 5, 8, 13, 21]); expect(methodology.configuration.requiredFields).toContain('storyPoints'); expect(methodology.configuration.requiredFields).toContain('acceptanceCriteria'); }); it('should set appropriate defaults for kanban', async () => { const methodology = await methodologyManager.setMethodology('kanban', 'user123'); expect(methodology.configuration.wip_limits).toBeDefined(); expect(methodology.configuration.wip_limits['In Progress']).toBe(3); expect(methodology.configuration.cycleTimeTracking).toBe(true); expect(methodology.configuration.leadTimeTracking).toBe(true); expect(methodology.configuration.requiredFields).not.toContain('storyPoints'); }); }); });