import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { IdentityDB } from '../src/core/identity-db'; async function seedMemoryGraph(db: IdentityDB, spaceName?: string): Promise { await db.addFact({ statement: 'I have worked with TypeScript since 2025.', spaceName, topics: [ { name: 'I', category: 'entity', granularity: 'concrete', role: 'subject' }, { name: 'TypeScript', category: 'entity', granularity: 'concrete', role: 'object' }, { name: '2025', category: 'temporal', granularity: 'concrete', role: 'time' }, ], }); await db.addFact({ statement: 'TypeScript is a programming language.', spaceName, topics: [ { name: 'TypeScript', category: 'entity', granularity: 'concrete', role: 'subject' }, { name: 'programming language', category: 'concept', granularity: 'abstract', role: 'classification' }, ], }); await db.linkTopics({ parentName: 'software technology', childName: 'programming language', spaceName, }); await db.linkTopics({ parentName: 'programming language', childName: 'TypeScript', spaceName, }); } describe('IdentityDB queries', () => { let db: IdentityDB; beforeEach(async () => { db = await IdentityDB.connect({ client: 'sqlite', filename: ':memory:' }); await db.initialize(); await seedMemoryGraph(db); }); afterEach(async () => { await db.close(); }); it('gets a topic with its facts', async () => { const topic = await db.getTopicByName('TypeScript', { includeFacts: true }); expect(topic).not.toBeNull(); expect(topic?.name).toBe('TypeScript'); expect(topic?.facts).toHaveLength(2); expect(topic?.facts.map((fact) => fact.statement)).toEqual([ 'I have worked with TypeScript since 2025.', 'TypeScript is a programming language.', ]); }); it('gets only the facts linked to another topic', async () => { const facts = await db.getTopicFactsLinkedTo('TypeScript', '2025'); expect(facts).toHaveLength(1); expect(facts[0]?.statement).toBe('I have worked with TypeScript since 2025.'); }); it('lists topics without expanding facts', async () => { const topics = await db.listTopics({ includeFacts: false }); expect(topics.map((topic) => topic.name)).toEqual([ '2025', 'I', 'programming language', 'software technology', 'TypeScript', ]); expect('facts' in topics[0]!).toBe(false); }); it('finds connected topics with shared fact counts', async () => { const connectedTopics = await db.findConnectedTopics('TypeScript'); expect(connectedTopics).toEqual([ expect.objectContaining({ name: '2025', sharedFactCount: 1 }), expect.objectContaining({ name: 'I', sharedFactCount: 1 }), expect.objectContaining({ name: 'programming language', sharedFactCount: 1 }), ]); }); it('finds facts that connect all requested topics', async () => { const facts = await db.findFactsConnectingTopics(['I', 'TypeScript', '2025']); expect(facts).toHaveLength(1); expect(facts[0]?.statement).toBe('I have worked with TypeScript since 2025.'); }); it('lists direct child topics for a parent topic', async () => { const children = await db.getTopicChildren('programming language'); expect(children.map((topic) => topic.name)).toEqual(['TypeScript']); }); it('lists direct parent topics for a child topic', async () => { const parents = await db.getTopicParents('TypeScript'); expect(parents.map((topic) => topic.name)).toEqual(['programming language']); }); it('returns lineage from nearest parent outward', async () => { const lineage = await db.getTopicLineage('TypeScript'); expect(lineage.map((topic) => topic.name)).toEqual([ 'programming language', 'software technology', ]); }); it('keeps hierarchy and fact queries isolated per space', async () => { const isolatedDb = await IdentityDB.connect({ client: 'sqlite', filename: ':memory:' }); try { await isolatedDb.initialize(); await seedMemoryGraph(isolatedDb, 'A'); await isolatedDb.addFact({ statement: 'TypeScript is a typed superset.', spaceName: 'B', topics: [ { name: 'TypeScript', category: 'entity', granularity: 'concrete', role: 'subject' }, { name: 'superset', category: 'concept', granularity: 'abstract', role: 'classification' }, ], }); await isolatedDb.linkTopics({ parentName: 'language family', childName: 'TypeScript', spaceName: 'B', }); const alphaTopic = await isolatedDb.getTopicByName('TypeScript', { includeFacts: true, spaceName: 'A', }); const betaTopic = await isolatedDb.getTopicByName('TypeScript', { includeFacts: true, spaceName: 'B', }); const alphaParents = await isolatedDb.getTopicParents('TypeScript', { spaceName: 'A' }); const betaParents = await isolatedDb.getTopicParents('TypeScript', { spaceName: 'B' }); const alphaConnected = await isolatedDb.findConnectedTopics('TypeScript', { spaceName: 'A' }); const betaConnected = await isolatedDb.findConnectedTopics('TypeScript', { spaceName: 'B' }); expect(alphaTopic?.facts.map((fact) => fact.statement)).toEqual([ 'I have worked with TypeScript since 2025.', 'TypeScript is a programming language.', ]); expect(betaTopic?.facts.map((fact) => fact.statement)).toEqual([ 'TypeScript is a typed superset.', ]); expect(alphaParents.map((topic) => topic.name)).toEqual(['programming language']); expect(betaParents.map((topic) => topic.name)).toEqual(['language family']); expect(alphaConnected.map((topic) => topic.name)).toEqual(['2025', 'I', 'programming language']); expect(betaConnected.map((topic) => topic.name)).toEqual(['superset']); } finally { await isolatedDb.close(); } }); it('resolves alias names in topic lookups', async () => { await db.addTopicAlias('TypeScript', 'TS'); const topic = await db.getTopicByName('ts'); expect(topic?.name).toBe('TypeScript'); }); });