task-master-neo-sdlc
Version:
Enhanced task management system with Neo SDLC agents and MCP tools for comprehensive, AI-driven software development lifecycle management.
171 lines (140 loc) • 7.62 kB
JavaScript
import { ArchitectureValidatorAgent } from '../architecture-validator';
import { KnowledgeGraph } from '../../knowledge-graph'; // Adjust path
import { AgentWorkflowSystem } from '../../agent-workflow'; // Adjust path
// Mocks
const mockKnowledgeGraph = {
addNode: jest.fn(),
findNodes: jest.fn(),
findEdges: jest.fn(),
updateContext: jest.fn()
};
const mockWorkflow = {};
// Spy on the actual rule methods to verify they are called
let checkNoDirectDbFromFrontendSpy;
let checkApiUsageSpy;
describe('ArchitectureValidatorAgent', () => {
let agent;
beforeEach(() => {
jest.clearAllMocks();
agent = new ArchitectureValidatorAgent(mockKnowledgeGraph, mockWorkflow);
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
// Spy on rule methods *after* agent instantiation
checkNoDirectDbFromFrontendSpy = jest.spyOn(agent, 'checkNoDirectDbFromFrontend');
checkApiUsageSpy = jest.spyOn(agent, 'checkApiUsage');
});
afterEach(() => {
console.log.mockRestore();
console.error.mockRestore();
checkNoDirectDbFromFrontendSpy.mockRestore();
checkApiUsageSpy.mockRestore();
});
it('should validate architecture against all rules by default', async () => {
checkNoDirectDbFromFrontendSpy.mockResolvedValue([]); // Rule passes
checkApiUsageSpy.mockResolvedValue([]); // Rule passes
mockKnowledgeGraph.addNode.mockResolvedValue(undefined);
const report = await agent.validateArchitecture();
expect(report).toBeDefined();
expect(report.isCompliant).toBe(true);
expect(report.violations).toEqual([]);
expect(report.passedChecks).toEqual(['rule-no-direct-db-from-frontend', 'rule-api-gateway-pattern']);
expect(report.rulesChecked).toEqual(['rule-no-direct-db-from-frontend', 'rule-api-gateway-pattern']);
expect(checkNoDirectDbFromFrontendSpy).toHaveBeenCalledTimes(1);
expect(checkApiUsageSpy).toHaveBeenCalledTimes(1);
expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({
id: expect.stringMatching(/^architectureValidation:/),
type: 'architecture_validation_report',
data: report
});
});
it('should only validate specified rules', async () => {
checkNoDirectDbFromFrontendSpy.mockResolvedValue([]); // Rule passes
// checkApiUsageSpy is NOT mocked to return, so it won't be called
mockKnowledgeGraph.addNode.mockResolvedValue(undefined);
const report = await agent.validateArchitecture(['rule-no-direct-db-from-frontend']);
expect(report.isCompliant).toBe(true);
expect(report.violations).toEqual([]);
expect(report.passedChecks).toEqual(['rule-no-direct-db-from-frontend']);
expect(report.rulesChecked).toEqual(['rule-no-direct-db-from-frontend']);
expect(checkNoDirectDbFromFrontendSpy).toHaveBeenCalledTimes(1);
expect(checkApiUsageSpy).not.toHaveBeenCalled();
expect(mockKnowledgeGraph.addNode).toHaveBeenCalled();
});
it('should report violations from rules', async () => {
const violationDetail = 'Frontend component component:button depends on DB schema db_schema:users';
checkNoDirectDbFromFrontendSpy.mockResolvedValue([violationDetail]); // Rule fails
checkApiUsageSpy.mockResolvedValue([]); // Rule passes
mockKnowledgeGraph.addNode.mockResolvedValue(undefined);
const report = await agent.validateArchitecture();
expect(report.isCompliant).toBe(false);
expect(report.passedChecks).toEqual(['rule-api-gateway-pattern']);
expect(report.violations).toHaveLength(1);
expect(report.violations[0]).toEqual({
ruleId: 'rule-no-direct-db-from-frontend',
description: 'Frontend components should not directly depend on DB schemas.',
details: violationDetail
});
expect(checkNoDirectDbFromFrontendSpy).toHaveBeenCalledTimes(1);
expect(checkApiUsageSpy).toHaveBeenCalledTimes(1);
expect(mockKnowledgeGraph.addNode).toHaveBeenCalled();
});
it('should handle errors during rule execution', async () => {
const error = new Error('KG query failed');
checkNoDirectDbFromFrontendSpy.mockRejectedValue(error); // Rule throws error
checkApiUsageSpy.mockResolvedValue([]); // Rule passes
mockKnowledgeGraph.addNode.mockResolvedValue(undefined);
const report = await agent.validateArchitecture();
expect(report.isCompliant).toBe(false);
expect(report.passedChecks).toEqual(['rule-api-gateway-pattern']);
expect(report.violations).toHaveLength(1);
expect(report.violations[0]).toEqual({
ruleId: 'rule-no-direct-db-from-frontend',
description: 'Frontend components should not directly depend on DB schemas.',
details: `Error during check: ${error.message}`
});
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Error executing rule rule-no-direct-db-from-frontend'), error);
expect(checkNoDirectDbFromFrontendSpy).toHaveBeenCalledTimes(1);
expect(checkApiUsageSpy).toHaveBeenCalledTimes(1);
expect(mockKnowledgeGraph.addNode).toHaveBeenCalled();
});
// --- Tests for specific rule logic ---
describe('checkNoDirectDbFromFrontend rule logic', () => {
// Test the rule method directly, mocking KG calls it makes
it('should find no violations when no direct dependencies exist', async () => {
const mockComponents = [{ id: 'component:A', data: { dependencies: ['component:B'] } }];
const mockDbSchemas = [{ id: 'db_schema:users' }];
const mockEdges = [{ source: 'component:A', target: 'component:B', relationship: 'depends_on' }];
mockKnowledgeGraph.findNodes
.mockImplementationOnce(async ({ type }) => type === 'design-component' ? mockComponents : [])
.mockImplementationOnce(async ({ type }) => type === 'db_schema' ? mockDbSchemas : []);
mockKnowledgeGraph.findEdges.mockResolvedValue(mockEdges);
const violations = await agent.checkNoDirectDbFromFrontend();
expect(violations).toEqual([]);
});
it('should detect violation via direct edge', async () => {
const mockComponents = [{ id: 'component:C', data: {} }];
const mockDbSchemas = [{ id: 'db_schema:products' }];
const mockEdges = [{ source: 'component:C', target: 'db_schema:products', relationship: 'depends_on' }];
mockKnowledgeGraph.findNodes
.mockResolvedValueOnce(mockComponents)
.mockResolvedValueOnce(mockDbSchemas);
mockKnowledgeGraph.findEdges.mockResolvedValue(mockEdges);
const violations = await agent.checkNoDirectDbFromFrontend();
expect(violations).toHaveLength(1);
expect(violations[0]).toContain('component:C directly depends on DB schema db_schema:products');
});
it('should detect violation via dependency array in data', async () => {
const mockComponents = [{ id: 'component:D', data: { dependencies: ['db_schema:orders'] } }];
const mockDbSchemas = [{ id: 'db_schema:orders' }];
mockKnowledgeGraph.findNodes
.mockResolvedValueOnce(mockComponents)
.mockResolvedValueOnce(mockDbSchemas);
mockKnowledgeGraph.findEdges.mockResolvedValue([]); // No direct edge
const violations = await agent.checkNoDirectDbFromFrontend();
expect(violations).toHaveLength(1);
expect(violations[0]).toContain('component:D lists direct dependency on DB schema db_schema:orders');
});
});
// Add tests for checkApiUsage rule logic similarly...
// describe('checkApiUsage rule logic', () => { ... });
});