/** * Tests for Graph Utilities */ import { describe, it, expect } from 'vitest'; import { getNodeColor, getNodeRadius, calculateNodeDegree, getConnectedNodes, findHubNodes, filterNodesByType, getUniqueNodeTypes, getUniquePredicates, calculateGraphStats, formatNodeForExport, formatLinkForExport, } from '@/lib/rdf/graph-utils'; import type { GraphNode, GraphLink } from '@/types/rdf'; describe('Graph Utilities - Node Operations', () => { const sampleNodes: GraphNode[] = [ { id: 'node1', label: 'Node 1', uri: 'http://example.org/node1', type: 'Record' }, { id: 'node2', label: 'Node 2', uri: 'http://example.org/node2', type: 'Person' }, { id: 'node3', label: 'Node 3', uri: 'http://example.org/node3', type: 'Place' }, { id: 'node4', label: 'Node 4', uri: 'http://example.org/node4', type: 'Record' }, ]; const sampleLinks: GraphLink[] = [ { source: 'node1', target: 'node2', predicate: 'author', value: 1, isReversed: false, isBidirectional: false, }, { source: 'node1', target: 'node3', predicate: 'place', value: 1, isReversed: false, isBidirectional: false, }, { source: 'node2', target: 'node3', predicate: 'located at', value: 1, isReversed: false, isBidirectional: false, }, ]; it('should get node color by type', () => { expect(getNodeColor('Record')).toBe('#4A90E2'); expect(getNodeColor('Person')).toBe('#F39C12'); expect(getNodeColor('Place')).toBe('#2ECC71'); }); it('should calculate node radius based on connections', () => { const radius1 = getNodeRadius(sampleNodes[0], sampleLinks); const radius2 = getNodeRadius(sampleNodes[1], sampleLinks); expect(radius1).toBeGreaterThan(8); expect(radius2).toBeGreaterThan(8); }); it('should filter nodes by type', () => { const records = filterNodesByType(sampleNodes, ['Record']); expect(records).toHaveLength(2); expect(records.every((n) => n.type === 'Record')).toBe(true); const people = filterNodesByType(sampleNodes, ['Person']); expect(people).toHaveLength(1); }); it('should get unique node types', () => { const types = getUniqueNodeTypes(sampleNodes); expect(types).toContain('Record'); expect(types).toContain('Person'); expect(types).toContain('Place'); expect(types).toHaveLength(3); }); it('should format node for export', () => { const exported = formatNodeForExport(sampleNodes[0]); expect(exported).toHaveProperty('id'); expect(exported).toHaveProperty('label'); expect(exported).toHaveProperty('uri'); expect(exported).toHaveProperty('type'); }); }); describe('Graph Utilities - Link Operations', () => { const sampleLinks: GraphLink[] = [ { source: 'node1', target: 'node2', predicate: 'author', value: 1, isReversed: false, isBidirectional: false, }, { source: 'node1', target: 'node3', predicate: 'place', value: 1, isReversed: false, isBidirectional: false, }, { source: 'node2', target: 'node3', predicate: 'author', value: 1, isReversed: false, isBidirectional: false, }, ]; it('should get unique predicates', () => { const predicates = getUniquePredicates(sampleLinks); expect(predicates).toContain('author'); expect(predicates).toContain('place'); expect(predicates).toHaveLength(2); }); it('should format link for export', () => { const exported = formatLinkForExport(sampleLinks[0]); expect(exported).toHaveProperty('source'); expect(exported).toHaveProperty('target'); expect(exported).toHaveProperty('predicate'); }); }); describe('Graph Utilities - Node Degree', () => { const sampleLinks: GraphLink[] = [ { source: 'node1', target: 'node2', predicate: 'p1', value: 1, isReversed: false, isBidirectional: false }, { source: 'node1', target: 'node3', predicate: 'p2', value: 1, isReversed: false, isBidirectional: false }, { source: 'node2', target: 'node3', predicate: 'p3', value: 1, isReversed: false, isBidirectional: false }, { source: 'node4', target: 'node1', predicate: 'p4', value: 1, isReversed: false, isBidirectional: false }, ]; it('should calculate node degree', () => { const degree1 = calculateNodeDegree('node1', sampleLinks); expect(degree1.outDegree).toBe(2); expect(degree1.inDegree).toBe(1); expect(degree1.totalDegree).toBe(3); const degree2 = calculateNodeDegree('node2', sampleLinks); expect(degree2.outDegree).toBe(1); expect(degree2.inDegree).toBe(1); expect(degree2.totalDegree).toBe(2); }); it('should get connected nodes', () => { const connections = getConnectedNodes('node1', sampleLinks); expect(connections.outgoing).toContain('node2'); expect(connections.outgoing).toContain('node3'); expect(connections.incoming).toContain('node4'); }); it('should find hub nodes', () => { const sampleNodes: GraphNode[] = [ { id: 'node1', label: 'Node 1', uri: 'uri1', type: 'Record' }, { id: 'node2', label: 'Node 2', uri: 'uri2', type: 'Record' }, { id: 'node3', label: 'Node 3', uri: 'uri3', type: 'Record' }, { id: 'node4', label: 'Node 4', uri: 'uri4', type: 'Record' }, ]; const hubs = findHubNodes(sampleNodes, sampleLinks, 2); expect(hubs.length).toBeGreaterThan(0); }); }); describe('Graph Statistics', () => { const sampleNodes: GraphNode[] = [ { id: 'node1', label: 'Node 1', uri: 'uri1', type: 'Record' }, { id: 'node2', label: 'Node 2', uri: 'uri2', type: 'Person' }, { id: 'node3', label: 'Node 3', uri: 'uri3', type: 'Record' }, ]; const sampleLinks: GraphLink[] = [ { source: 'node1', target: 'node2', predicate: 'author', value: 1, isReversed: false, isBidirectional: false }, { source: 'node1', target: 'node3', predicate: 'part of', value: 1, isReversed: false, isBidirectional: false }, ]; it('should calculate graph statistics', () => { const stats = calculateGraphStats(sampleNodes, sampleLinks); expect(stats.nodeCount).toBe(3); expect(stats.linkCount).toBe(2); expect(stats.nodeTypes['Record']).toBe(2); expect(stats.nodeTypes['Person']).toBe(1); expect(stats.predicates['author']).toBe(1); expect(stats.predicates['part of']).toBe(1); expect(stats.avgDegree).toBeGreaterThan(0); }); });