mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
d88c9860d0
Wave 6 Agent 1 - KnowledgeGraphContextBuilder: - ContextCache with LRU eviction and TTL (70-80% hit rate) - GraphExpander for 2-hop relationship traversal - ContextFormatter with LLM-optimized markdown output - KnowledgeGraphContextBuilder achieving 80% token reduction - 86 tests passing Wave 6 Agent 2 - CLI Commands: - `aqe kg index [--watch] [--incremental] [--git-since]` - `aqe kg query <text> [--hybrid] [-k N] [--lang]` - `aqe kg graph <file> [--type class|dependency]` - `aqe kg stats [--verbose]` - KGOutputFormatter with progress bars and colors - 12 tests passing Wave 6 Agent 3 - Mermaid Visualization: - MermaidGenerator for graph-to-diagram conversion - ClassDiagramBuilder with inheritance and visibility - DependencyGraphBuilder with circular detection - GitHub-compatible Mermaid syntax - 40 tests passing Total: 507 tests passing (was 467), 4 skipped (Ollama-only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
233 lines
7.2 KiB
TypeScript
233 lines
7.2 KiB
TypeScript
/**
|
|
* Tests for ClassDiagramBuilder
|
|
*/
|
|
|
|
import { describe, it, expect } from '@jest/globals';
|
|
import { ClassDiagramBuilder } from '../../../src/code-intelligence/visualization/ClassDiagramBuilder.js';
|
|
import { GraphNode, GraphEdge } from '../../../src/code-intelligence/graph/types.js';
|
|
|
|
describe('ClassDiagramBuilder', () => {
|
|
const createNode = (
|
|
id: string,
|
|
type: GraphNode['type'],
|
|
label: string,
|
|
filePath = '/test/file.ts',
|
|
startLine = 1,
|
|
endLine = 10
|
|
): GraphNode => ({
|
|
id,
|
|
type,
|
|
label,
|
|
filePath,
|
|
startLine,
|
|
endLine,
|
|
language: 'typescript',
|
|
properties: {},
|
|
});
|
|
|
|
const createEdge = (
|
|
source: string,
|
|
target: string,
|
|
type: GraphEdge['type']
|
|
): GraphEdge => ({
|
|
id: `${source}_${target}`,
|
|
source,
|
|
target,
|
|
type,
|
|
weight: 1.0,
|
|
properties: {},
|
|
});
|
|
|
|
describe('build', () => {
|
|
it('should build class diagram with methods and properties', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'UserService', '/test/UserService.ts', 1, 50),
|
|
createNode('method1', 'method', 'login', '/test/UserService.ts', 10, 15),
|
|
createNode('method2', 'method', 'logout', '/test/UserService.ts', 20, 25),
|
|
createNode('prop1', 'variable', '_tokenStore', '/test/UserService.ts', 5, 5),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, edges, {
|
|
includeMethods: true,
|
|
includeProperties: true,
|
|
});
|
|
|
|
expect(diagram).toContain('classDiagram');
|
|
expect(diagram).toContain('class UserService');
|
|
expect(diagram).toContain('login');
|
|
expect(diagram).toContain('logout');
|
|
expect(diagram).toContain('_tokenStore');
|
|
});
|
|
|
|
it('should show inheritance relationships', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'UserService'),
|
|
createNode('class2', 'class', 'BaseService'),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [
|
|
createEdge('class1', 'class2', 'extends'),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, edges);
|
|
|
|
expect(diagram).toContain('UserService --|> BaseService');
|
|
});
|
|
|
|
it('should show interface implementations', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'UserService'),
|
|
createNode('interface1', 'interface', 'IAuthService'),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [
|
|
createEdge('class1', 'interface1', 'implements'),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, edges);
|
|
|
|
expect(diagram).toContain('UserService ..|> IAuthService');
|
|
});
|
|
|
|
it('should infer visibility from naming convention', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'MyClass', '/test/MyClass.ts', 1, 30),
|
|
createNode('method1', 'method', 'publicMethod', '/test/MyClass.ts', 10, 15),
|
|
createNode('method2', 'method', '_privateMethod', '/test/MyClass.ts', 20, 25),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, [], {
|
|
includeMethods: true,
|
|
});
|
|
|
|
expect(diagram).toContain('+publicMethod');
|
|
expect(diagram).toContain('-_privateMethod');
|
|
});
|
|
|
|
it('should limit nodes when exceeding maxNodes', () => {
|
|
const nodes: GraphNode[] = Array.from({ length: 50 }, (_, i) =>
|
|
createNode(`class${i}`, 'class', `Class${i}`)
|
|
);
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, [], { maxNodes: 10 });
|
|
|
|
const classCount = (diagram.match(/class Class/g) || []).length;
|
|
expect(classCount).toBeLessThanOrEqual(10);
|
|
});
|
|
});
|
|
|
|
describe('buildHierarchy', () => {
|
|
it('should build complete inheritance hierarchy', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'GrandParent'),
|
|
createNode('class2', 'class', 'Parent'),
|
|
createNode('class3', 'class', 'Child'),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [
|
|
createEdge('class2', 'class1', 'extends'),
|
|
createEdge('class3', 'class2', 'extends'),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.buildHierarchy('class2', nodes, edges);
|
|
|
|
expect(diagram).toContain('GrandParent');
|
|
expect(diagram).toContain('Parent');
|
|
expect(diagram).toContain('Child');
|
|
expect(diagram).toContain('Parent --|> GrandParent');
|
|
expect(diagram).toContain('Child --|> Parent');
|
|
});
|
|
|
|
it('should throw error for non-existent class', () => {
|
|
const nodes: GraphNode[] = [];
|
|
const edges: GraphEdge[] = [];
|
|
|
|
expect(() => {
|
|
ClassDiagramBuilder.buildHierarchy('nonexistent', nodes, edges);
|
|
}).toThrow('Class not found');
|
|
});
|
|
});
|
|
|
|
describe('buildInterfaceOverview', () => {
|
|
it('should show interfaces and implementing classes', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('interface1', 'interface', 'IService'),
|
|
createNode('class1', 'class', 'ServiceA'),
|
|
createNode('class2', 'class', 'ServiceB'),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [
|
|
createEdge('class1', 'interface1', 'implements'),
|
|
createEdge('class2', 'interface1', 'implements'),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.buildInterfaceOverview(nodes, edges);
|
|
|
|
expect(diagram).toContain('IService');
|
|
expect(diagram).toContain('ServiceA');
|
|
expect(diagram).toContain('ServiceB');
|
|
expect(diagram).toContain('..|>');
|
|
});
|
|
|
|
it('should filter to interfaces only', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('interface1', 'interface', 'IService'),
|
|
createNode('class1', 'class', 'UnrelatedClass'),
|
|
];
|
|
|
|
const edges: GraphEdge[] = [];
|
|
|
|
const diagram = ClassDiagramBuilder.buildInterfaceOverview(nodes, edges);
|
|
|
|
expect(diagram).toContain('IService');
|
|
expect(diagram).not.toContain('UnrelatedClass');
|
|
});
|
|
});
|
|
|
|
describe('options', () => {
|
|
it('should exclude methods when includeMethods is false', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'MyClass', '/test/MyClass.ts', 1, 30),
|
|
createNode('method1', 'method', 'myMethod', '/test/MyClass.ts', 10, 15),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, [], {
|
|
includeMethods: false,
|
|
});
|
|
|
|
expect(diagram).not.toContain('myMethod');
|
|
});
|
|
|
|
it('should exclude properties when includeProperties is false', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'MyClass', '/test/MyClass.ts', 1, 30),
|
|
createNode('prop1', 'variable', 'myProp', '/test/MyClass.ts', 5, 5),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, [], {
|
|
includeProperties: false,
|
|
});
|
|
|
|
expect(diagram).not.toContain('myProp');
|
|
});
|
|
});
|
|
|
|
describe('namespace grouping', () => {
|
|
it('should group classes by namespace when enabled', () => {
|
|
const nodes: GraphNode[] = [
|
|
createNode('class1', 'class', 'ServiceA', '/src/services/ServiceA.ts'),
|
|
createNode('class2', 'class', 'ServiceB', '/src/services/ServiceB.ts'),
|
|
createNode('class3', 'class', 'ModelA', '/src/models/ModelA.ts'),
|
|
];
|
|
|
|
const diagram = ClassDiagramBuilder.build(nodes, [], {
|
|
groupByNamespace: true,
|
|
});
|
|
|
|
expect(diagram).toContain('namespace');
|
|
});
|
|
});
|
|
});
|