import { describe, it, expect } from 'vitest'; import { GraphEngine } from '../graph/GraphEngine'; import type { GraphNode, GraphEdge } from '../adapters/types'; import { setupPinia } from './test-helpers'; import { useVocabularyStore } from '../stores/vocabulary'; import { getFactory, resetFactory } from '../adapters/factory'; import { ref, shallowRef, isReactive, isRef, toRaw } from 'vue'; function makeNode(uri: string, conceptId: string, register = 'test'): GraphNode { return { uri, register, conceptId, designations: { eng: conceptId }, status: 'valid', loaded: true }; } function makeEdge(source: string, target: string, type = 'references', register = 'test'): GraphEdge { return { source, target, type, register }; } describe('shallowRef graph — no deep proxy', () => { it('graph is shallowRef not deep ref', () => { setupPinia(); resetFactory(); const store = useVocabularyStore(); const raw = toRaw(store.graph); expect(raw).toBeInstanceOf(GraphEngine); expect(raw.nodeCount).toBe(0); }); it('graph mutations work through shallowRef', () => { setupPinia(); resetFactory(); const store = useVocabularyStore(); const engine = store.graph; engine.addNode(makeNode('uri:a', 'a')); engine.addEdge(makeEdge('uri:a', 'uri:b')); expect(engine.nodeCount).toBe(2); expect(engine.edgeCount).toBe(1); }); }); describe('LRU conceptCache', () => { it('cache evicts oldest entries beyond MAX_CACHE', () => { // This tests the LRU behavior indirectly through the adapter // The MAX_CACHE is 100, but we verify the mechanism works const cache = new Map(); const MAX = 5; function set(key: string, value: string) { cache.set(key, value); if (cache.size > MAX) { const oldest = cache.keys().next().value; if (oldest !== undefined) cache.delete(oldest); } } set('a', '1'); set('b', '2'); set('c', '3'); set('d', '4'); set('e', '5'); expect(cache.size).toBe(5); set('f', '6'); expect(cache.size).toBe(5); expect(cache.has('a')).toBe(false); expect(cache.get('f')).toBe('6'); }); it('cache promotes accessed entries', () => { const cache = new Map(); const MAX = 3; function set(key: string, value: string) { // Promote: delete and re-add to move to end cache.delete(key); cache.set(key, value); if (cache.size > MAX) { const oldest = cache.keys().next().value; if (oldest !== undefined) cache.delete(oldest); } } set('a', '1'); set('b', '2'); set('c', '3'); // Access 'a' to promote it set('a', '1'); // Add new entry — 'b' should be evicted, not 'a' set('d', '4'); expect(cache.has('a')).toBe(true); expect(cache.has('b')).toBe(false); expect(cache.has('c')).toBe(true); expect(cache.has('d')).toBe(true); }); });