feat: add neural extraction APIs with NounType taxonomy

Add brain.extract() and brain.extractConcepts() methods that use
NeuralEntityExtractor with embeddings and sophisticated NounType
taxonomy (30+ entity types) for semantic entity and concept extraction.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
David Snelling 2025-09-29 13:51:47 -07:00
parent 27cc699555
commit dd50d89ad6
41 changed files with 3807 additions and 7391 deletions

View file

@ -1,691 +0,0 @@
/**
* Knowledge Layer Tests
*
* Comprehensive tests for all Knowledge Layer components integrated with VFS:
* - EventRecorder
* - SemanticVersioning
* - PersistentEntitySystem
* - ConceptSystem
* - GitBridge
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { VirtualFileSystem } from '../../src/vfs/VirtualFileSystem.js'
import { EventRecorder } from '../../src/vfs/EventRecorder.js'
import { SemanticVersioning } from '../../src/vfs/SemanticVersioning.js'
import { PersistentEntitySystem } from '../../src/vfs/PersistentEntitySystem.js'
import { ConceptSystem } from '../../src/vfs/ConceptSystem.js'
import { GitBridge } from '../../src/vfs/GitBridge.js'
import { tmpdir } from 'os'
import { promises as fs } from 'fs'
import * as path from 'path'
describe('Knowledge Layer', () => {
let brain: Brainy
let vfs: VirtualFileSystem
let tempDir: string
beforeEach(async () => {
// Use in-memory storage for tests
brain = new Brainy({
storage: { type: 'memory' }
})
await brain.init()
vfs = new VirtualFileSystem(brain)
await vfs.init()
// Create temporary directory for Git tests
tempDir = await fs.mkdtemp(path.join(tmpdir(), 'brainy-knowledge-test-'))
})
afterEach(async () => {
// Clean up temporary directory
try {
await fs.rm(tempDir, { recursive: true, force: true })
} catch (error) {
// Ignore cleanup errors
}
})
describe('VFS Knowledge Integration', () => {
it('should have VFS integrated with Brainy', async () => {
// VFS should be properly integrated with Brainy
expect(vfs).toBeDefined()
expect(brain.vfs()).toBeDefined()
// Test basic VFS operations
const exists = await vfs.exists('/')
expect(exists).toBe(true)
})
it('should support knowledge layer operations through VFS', async () => {
// Test that VFS supports file operations that the knowledge layer depends on
await vfs.writeFile('/test.txt', 'test content')
const content = await vfs.readFile('/test.txt')
expect(content.toString()).toBe('test content')
// Test that file exists
const exists = await vfs.exists('/test.txt')
expect(exists).toBe(true)
})
})
describe('EventRecorder', () => {
let eventRecorder: EventRecorder
beforeEach(() => {
eventRecorder = new EventRecorder(brain)
})
it('should record file events', async () => {
const eventId = await eventRecorder.recordEvent({
type: 'write',
path: '/test.txt',
content: Buffer.from('hello world'),
size: 11,
author: 'test-user'
})
expect(eventId).toBeDefined()
})
it('should retrieve file history', async () => {
// Record multiple events
await eventRecorder.recordEvent({
type: 'create',
path: '/test.txt',
content: Buffer.from('initial'),
size: 7
})
await eventRecorder.recordEvent({
type: 'write',
path: '/test.txt',
content: Buffer.from('updated'),
size: 7
})
const history = await eventRecorder.getHistory('/test.txt')
expect(history).toHaveLength(2)
expect(history[0].type).toBe('write') // Newest first
expect(history[1].type).toBe('create')
})
it('should reconstruct file at specific time', async () => {
const timestamp1 = Date.now()
await eventRecorder.recordEvent({
type: 'create',
path: '/test.txt',
content: Buffer.from('version 1'),
size: 9
})
// Wait a moment
await new Promise(resolve => setTimeout(resolve, 10))
const timestamp2 = Date.now()
await eventRecorder.recordEvent({
type: 'write',
path: '/test.txt',
content: Buffer.from('version 2'),
size: 9
})
// Reconstruct at timestamp1 (should get version 1)
const content1 = await eventRecorder.reconstructFileAtTime('/test.txt', timestamp1)
expect(content1?.toString()).toBe('version 1')
// Reconstruct at timestamp2 (should get version 2)
const content2 = await eventRecorder.reconstructFileAtTime('/test.txt', timestamp2)
expect(content2?.toString()).toBe('version 2')
})
it('should calculate statistics', async () => {
await eventRecorder.recordEvent({
type: 'write',
path: '/test.txt',
size: 100,
author: 'user1'
})
await eventRecorder.recordEvent({
type: 'append',
path: '/test.txt',
size: 50,
author: 'user2'
})
const stats = await eventRecorder.getStatistics('/test.txt')
expect(stats.totalEvents).toBe(2)
expect(stats.totalWrites).toBe(2)
expect(stats.totalBytes).toBe(150)
expect(stats.authors).toContain('user1')
expect(stats.authors).toContain('user2')
})
it('should find temporal coupling', async () => {
const now = Date.now()
// Record related events within time window
await eventRecorder.recordEvent({
type: 'write',
path: '/file1.txt'
})
await eventRecorder.recordEvent({
type: 'write',
path: '/file2.txt'
})
const coupling = await eventRecorder.findTemporalCoupling('/file1.txt', 60000)
expect(coupling.has('/file2.txt')).toBe(true)
expect(coupling.get('/file2.txt')).toBe(1)
})
})
describe('SemanticVersioning', () => {
let semanticVersioning: SemanticVersioning
beforeEach(() => {
semanticVersioning = new SemanticVersioning(brain, {
threshold: 0.3,
maxVersions: 5
})
})
it('should detect when versioning is needed', async () => {
const oldContent = Buffer.from('hello world')
const newContent = Buffer.from('goodbye world')
const shouldVersion = await semanticVersioning.shouldVersion(oldContent, newContent)
// Should version due to content change (no embeddings available)
expect(shouldVersion).toBe(true)
})
it('should not version identical content', async () => {
const content = Buffer.from('hello world')
const shouldVersion = await semanticVersioning.shouldVersion(content, content)
expect(shouldVersion).toBe(false)
})
it('should create versions', async () => {
const content = Buffer.from('version 1 content')
const versionId = await semanticVersioning.createVersion(
'/test.txt',
content,
{ author: 'test-user', message: 'Initial version' }
)
expect(versionId).toBeDefined()
// Check version exists
const versions = await semanticVersioning.getVersions('/test.txt')
expect(versions).toHaveLength(1)
expect(versions[0].version).toBe(1)
expect(versions[0].author).toBe('test-user')
})
it('should retrieve version content', async () => {
const content = Buffer.from('test content')
const versionId = await semanticVersioning.createVersion('/test.txt', content)
const retrievedContent = await semanticVersioning.getVersion('/test.txt', versionId)
expect(retrievedContent).toEqual(content)
})
it('should provide version history', async () => {
// Create multiple versions
await semanticVersioning.createVersion('/test.txt', Buffer.from('v1'))
await semanticVersioning.createVersion('/test.txt', Buffer.from('v2'))
await semanticVersioning.createVersion('/test.txt', Buffer.from('v3'))
const history = await semanticVersioning.getVersionHistory('/test.txt')
expect(history).toHaveLength(3)
expect(history[0].version.version).toBe(3) // Newest first
expect(history[2].version.version).toBe(1) // Oldest last
})
it('should prune old versions', async () => {
// Create more versions than maxVersions
for (let i = 1; i <= 10; i++) {
await semanticVersioning.createVersion('/test.txt', Buffer.from(`version ${i}`))
}
const versions = await semanticVersioning.getVersions('/test.txt')
// Should have pruned to maxVersions (5)
expect(versions.length).toBeLessThanOrEqual(5)
})
})
describe('PersistentEntitySystem', () => {
let entitySystem: PersistentEntitySystem
beforeEach(() => {
entitySystem = new PersistentEntitySystem(brain, {
autoExtract: false,
evolutionTracking: true
})
})
it('should create persistent entities', async () => {
const entityId = await entitySystem.createEntity({
name: 'John Doe',
type: 'character',
aliases: ['Johnny', 'JD'],
attributes: { age: 30, role: 'protagonist' }
})
expect(entityId).toBeDefined()
})
it('should find entities by name', async () => {
await entitySystem.createEntity({
name: 'Alice Smith',
type: 'character',
aliases: ['Alice'],
attributes: {}
})
const entities = await entitySystem.findEntity({ name: 'Alice Smith' })
expect(entities).toHaveLength(1)
expect(entities[0].name).toBe('Alice Smith')
expect(entities[0].type).toBe('character')
})
it('should record entity appearances', async () => {
const entityId = await entitySystem.createEntity({
name: 'API Gateway',
type: 'service',
aliases: [],
attributes: {}
})
const appearanceId = await entitySystem.recordAppearance(
entityId,
'/docs/architecture.md',
'The API Gateway handles all incoming requests...',
{ confidence: 0.9 }
)
expect(appearanceId).toBeDefined()
// Check appearances
const appearances = await entitySystem.findAppearances(entityId)
expect(appearances).toHaveLength(1)
expect(appearances[0].filePath).toBe('/docs/architecture.md')
expect(appearances[0].confidence).toBe(0.9)
})
it('should track entity evolution', async () => {
const entityId = await entitySystem.createEntity({
name: 'UserService',
type: 'service',
aliases: [],
attributes: { version: '1.0' }
})
// Evolve the entity
await entitySystem.evolveEntity(
entityId,
{ attributes: { version: '2.0', newFeature: true } },
'/src/user-service.ts',
'Added new feature'
)
const { entity, timeline } = await entitySystem.getEvolution(entityId)
expect(entity.version).toBeGreaterThan(1)
expect(entity.attributes.version).toBe('2.0')
expect(entity.attributes.newFeature).toBe(true)
})
it('should extract entities from content', async () => {
entitySystem = new PersistentEntitySystem(brain, { autoExtract: true })
const content = Buffer.from(`
class UserService {
function authenticate(user) {
// Authentication logic
}
}
`)
const entityIds = await entitySystem.extractEntities('/src/user.ts', content)
expect(entityIds.length).toBeGreaterThan(0)
})
it('should update references when files move', async () => {
const entityId = await entitySystem.createEntity({
name: 'TestEntity',
type: 'test',
aliases: [],
attributes: {}
})
await entitySystem.recordAppearance(
entityId,
'/old/path.txt',
'test context'
)
// Move file
await entitySystem.updateReferences('/old/path.txt', '/new/path.txt')
// Check that appearances were updated
const appearances = await entitySystem.findAppearances(entityId)
expect(appearances[0].filePath).toBe('/new/path.txt')
})
})
describe('ConceptSystem', () => {
let conceptSystem: ConceptSystem
beforeEach(() => {
conceptSystem = new ConceptSystem(brain, {
autoLink: false,
similarityThreshold: 0.7
})
})
it('should create universal concepts', async () => {
const conceptId = await conceptSystem.createConcept({
name: 'Authentication',
domain: 'technical',
category: 'pattern',
keywords: ['auth', 'security', 'login'],
strength: 0.8,
metadata: {}
})
expect(conceptId).toBeDefined()
})
it('should find concepts by criteria', async () => {
await conceptSystem.createConcept({
name: 'User Experience',
domain: 'business',
category: 'concept',
keywords: ['UX', 'usability'],
strength: 0.9,
metadata: {}
})
const concepts = await conceptSystem.findConcepts({ domain: 'business' })
expect(concepts).toHaveLength(1)
expect(concepts[0].name).toBe('User Experience')
expect(concepts[0].domain).toBe('business')
})
it('should link concepts together', async () => {
const concept1Id = await conceptSystem.createConcept({
name: 'Authentication',
domain: 'technical',
category: 'pattern',
keywords: [],
strength: 0.8,
metadata: {}
})
const concept2Id = await conceptSystem.createConcept({
name: 'Security',
domain: 'technical',
category: 'concept',
keywords: [],
strength: 0.9,
metadata: {}
})
const linkId = await conceptSystem.linkConcept(
concept1Id,
concept2Id,
'related',
{ strength: 0.8, bidirectional: true }
)
expect(linkId).toBeDefined()
})
it('should record concept manifestations', async () => {
const conceptId = await conceptSystem.createConcept({
name: 'Dependency Injection',
domain: 'technical',
category: 'pattern',
keywords: [],
strength: 0.8,
metadata: {}
})
const manifestationId = await conceptSystem.recordManifestation(
conceptId,
'/src/di-container.ts',
'class DIContainer implements dependency injection pattern...',
'implementation',
{ confidence: 0.9 }
)
expect(manifestationId).toBeDefined()
// Check manifestations
const manifestations = await conceptSystem.findAppearances(conceptId)
expect(manifestations).toHaveLength(1)
expect(manifestations[0].form).toBe('implementation')
})
it('should extract concepts from content', async () => {
conceptSystem = new ConceptSystem(brain, { autoLink: true })
const content = Buffer.from(`
Authentication is a critical security concept.
The authentication service validates user credentials.
`)
const conceptIds = await conceptSystem.extractAndLinkConcepts('/docs/auth.md', content)
expect(conceptIds.length).toBeGreaterThan(0)
})
it('should generate concept graph', async () => {
// Create test concepts
const concept1Id = await conceptSystem.createConcept({
name: 'API Design',
domain: 'technical',
category: 'concept',
keywords: [],
strength: 0.8,
metadata: {}
})
const concept2Id = await conceptSystem.createConcept({
name: 'REST',
domain: 'technical',
category: 'pattern',
keywords: [],
strength: 0.7,
metadata: {}
})
await conceptSystem.linkConcept(concept1Id, concept2Id, 'uses')
const graph = await conceptSystem.getConceptGraph()
expect(graph.concepts).toHaveLength(2)
expect(graph.links).toHaveLength(1)
expect(graph.links[0].relationship).toBe('uses')
})
})
describe('GitBridge', () => {
let gitBridge: GitBridge
let gitRepoPath: string
beforeEach(async () => {
gitBridge = new GitBridge(vfs, brain)
gitRepoPath = path.join(tempDir, 'git-repo')
await fs.mkdir(gitRepoPath, { recursive: true })
})
it('should export VFS to Git repository', async () => {
// Create test files in VFS
await vfs.mkdir('/project')
await vfs.writeFile('/project/README.md', '# Test Project')
await vfs.writeFile('/project/src/main.js', 'console.log("Hello World")')
const gitRepo = await gitBridge.exportToGit(
'/project',
gitRepoPath,
{
preserveMetadata: true,
commitMessage: 'Initial export from VFS'
}
)
expect(gitRepo.files.length).toBeGreaterThan(0)
expect(gitRepo.commits).toHaveLength(1)
expect(gitRepo.commits[0].message).toBe('Initial export from VFS')
// Check files were created
const readmeExists = await fs.access(path.join(gitRepoPath, 'README.md')).then(() => true).catch(() => false)
const mainExists = await fs.access(path.join(gitRepoPath, 'src/main.js')).then(() => true).catch(() => false)
expect(readmeExists).toBe(true)
expect(mainExists).toBe(true)
})
it('should import Git repository to VFS', async () => {
// Create test Git repository structure
await fs.mkdir(path.join(gitRepoPath, 'src'), { recursive: true })
await fs.writeFile(path.join(gitRepoPath, 'README.md'), '# Imported Project')
await fs.writeFile(path.join(gitRepoPath, 'src/app.js'), 'const app = "hello"')
const stats = await gitBridge.importFromGit(
gitRepoPath,
'/imported'
)
expect(stats.filesImported).toBe(2)
// Check files were imported
const readmeExists = await vfs.exists('/imported/README.md')
const appExists = await vfs.exists('/imported/src/app.js')
expect(readmeExists).toBe(true)
expect(appExists).toBe(true)
// Check file content
const readmeContent = await vfs.readFile('/imported/README.md')
expect(readmeContent.toString()).toBe('# Imported Project')
})
it('should preserve metadata during export/import cycle', async () => {
// Create file with metadata
await vfs.writeFile('/test.txt', 'test content', {
metadata: { author: 'test-user', tags: ['important'] }
})
// Export to Git
await gitBridge.exportToGit('/', gitRepoPath, { preserveMetadata: true })
// Clear VFS
await vfs.unlink('/test.txt')
// Import back from Git
await gitBridge.importFromGit(gitRepoPath, '/restored', { extractMetadata: true })
// Check file was restored
const exists = await vfs.exists('/restored/test.txt')
expect(exists).toBe(true)
const content = await vfs.readFile('/restored/test.txt')
expect(content.toString()).toBe('test content')
})
it('should handle nested directory structures', async () => {
// Create nested structure
await vfs.mkdir('/deep/nested/structure', { recursive: true })
await vfs.writeFile('/deep/nested/structure/file.txt', 'deep file')
await vfs.writeFile('/deep/other.txt', 'other file')
// Export
await gitBridge.exportToGit('/deep', gitRepoPath)
// Import to new location
const stats = await gitBridge.importFromGit(gitRepoPath, '/imported')
expect(stats.filesImported).toBe(2)
const deepExists = await vfs.exists('/imported/nested/structure/file.txt')
const otherExists = await vfs.exists('/imported/other.txt')
expect(deepExists).toBe(true)
expect(otherExists).toBe(true)
})
})
describe('Integration Tests', () => {
it('should work with all knowledge layer components together', async () => {
// Test that individual knowledge components can work with VFS
const eventRecorder = new EventRecorder(brain)
const semanticVersioning = new SemanticVersioning(brain)
const entitySystem = new PersistentEntitySystem(brain)
const conceptSystem = new ConceptSystem(brain)
// Write a file through VFS
const content = `
# Authentication Service
The AuthService class handles user authentication.
It implements the security pattern for validating credentials.
class AuthService {
authenticate(user, password) {
// Validation logic
}
}
`
await vfs.writeFile('/services/auth.md', content)
// Test that the file exists in VFS
const exists = await vfs.exists('/services/auth.md')
expect(exists).toBe(true)
// Test that file content is correct
const readContent = await vfs.readFile('/services/auth.md')
expect(readContent.toString()).toContain('AuthService')
})
it('should maintain VFS consistency with file operations', async () => {
// Test basic VFS operations that knowledge layer depends on
await vfs.mkdir('/controllers', { recursive: true })
await vfs.writeFile('/controllers/user.ts', 'class UserController {}')
// Test file rename
await vfs.rename('/controllers/user.ts', '/controllers/user-controller.ts')
// Verify file still exists at new location
const exists = await vfs.exists('/controllers/user-controller.ts')
expect(exists).toBe(true)
// Verify old location doesn't exist
const oldExists = await vfs.exists('/controllers/user.ts')
expect(oldExists).toBe(false)
})
})
})

View file

@ -0,0 +1,424 @@
/**
* Tests for SemanticPathParser
* REAL TESTS - No mocks, pure logic testing
*/
import { describe, test, expect } from 'vitest'
import {
SemanticPathParser,
ParsedSemanticPath,
RelationshipValue,
SimilarityValue
} from '../../../src/vfs/semantic/SemanticPathParser.js'
describe('SemanticPathParser', () => {
const parser = new SemanticPathParser()
describe('Traditional Paths', () => {
test('parses simple traditional path', () => {
const result = parser.parse('/src/auth.ts')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/src/auth.ts')
expect(result.subpath).toBeUndefined()
})
test('parses nested traditional path', () => {
const result = parser.parse('/src/lib/utils/helper.ts')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/src/lib/utils/helper.ts')
})
test('parses root path', () => {
const result = parser.parse('/')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/')
})
test('normalizes paths with trailing slash', () => {
const result = parser.parse('/src/auth.ts/')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/src/auth.ts')
})
test('normalizes paths with multiple slashes', () => {
const result = parser.parse('//src///auth.ts')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/src/auth.ts')
})
test('adds leading slash if missing', () => {
const result = parser.parse('src/auth.ts')
expect(result.dimension).toBe('traditional')
expect(result.value).toBe('/src/auth.ts')
})
})
describe('Concept Paths', () => {
test('parses concept path without subpath', () => {
const result = parser.parse('/by-concept/authentication')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('authentication')
expect(result.subpath).toBeUndefined()
})
test('parses concept path with subpath', () => {
const result = parser.parse('/by-concept/authentication/login.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('authentication')
expect(result.subpath).toBe('login.ts')
})
test('parses concept path with nested subpath', () => {
const result = parser.parse('/by-concept/security/src/auth/login.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('security')
expect(result.subpath).toBe('src/auth/login.ts')
})
test('parses concept with dashes', () => {
const result = parser.parse('/by-concept/dependency-injection/service.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('dependency-injection')
expect(result.subpath).toBe('service.ts')
})
})
describe('Author Paths', () => {
test('parses author path without subpath', () => {
const result = parser.parse('/by-author/alice')
expect(result.dimension).toBe('author')
expect(result.value).toBe('alice')
expect(result.subpath).toBeUndefined()
})
test('parses author path with subpath', () => {
const result = parser.parse('/by-author/alice/code.ts')
expect(result.dimension).toBe('author')
expect(result.value).toBe('alice')
expect(result.subpath).toBe('code.ts')
})
test('parses author path with nested subpath', () => {
const result = parser.parse('/by-author/bob/projects/2024/file.ts')
expect(result.dimension).toBe('author')
expect(result.value).toBe('bob')
expect(result.subpath).toBe('projects/2024/file.ts')
})
})
describe('Time Paths', () => {
test('parses time path without subpath', () => {
const result = parser.parse('/as-of/2024-03-15')
expect(result.dimension).toBe('time')
expect(result.value).toBeInstanceOf(Date)
expect((result.value as Date).getFullYear()).toBe(2024)
expect((result.value as Date).getMonth()).toBe(2) // March is index 2
expect((result.value as Date).getDate()).toBe(15)
expect(result.subpath).toBeUndefined()
})
test('parses time path with subpath', () => {
const result = parser.parse('/as-of/2024-03-15/src/auth.ts')
expect(result.dimension).toBe('time')
expect(result.value).toBeInstanceOf(Date)
expect(result.subpath).toBe('src/auth.ts')
})
test('treats invalid date format as traditional path', () => {
// Doesn't match the time pattern, so becomes traditional
const result = parser.parse('/as-of/2024-03/file.ts')
expect(result.dimension).toBe('traditional')
})
test('treats invalid date components as traditional path', () => {
// Doesn't match the time pattern, so becomes traditional
const result = parser.parse('/as-of/invalid-date/file.ts')
expect(result.dimension).toBe('traditional')
})
test('throws on invalid year', () => {
expect(() => parser.parse('/as-of/1800-03-15/file.ts')).toThrow('Invalid year')
})
test('throws on invalid month', () => {
expect(() => parser.parse('/as-of/2024-13-15/file.ts')).toThrow('Invalid month')
})
test('throws on invalid day', () => {
expect(() => parser.parse('/as-of/2024-03-32/file.ts')).toThrow('Invalid day')
})
})
describe('Relationship Paths', () => {
test('parses relationship path without depth', () => {
const result = parser.parse('/related-to/src/auth.ts')
expect(result.dimension).toBe('relationship')
const value = result.value as RelationshipValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.depth).toBeUndefined()
expect(value.relationshipTypes).toBeUndefined()
})
test('parses relationship path with depth', () => {
const result = parser.parse('/related-to/src/auth.ts/depth-2')
expect(result.dimension).toBe('relationship')
const value = result.value as RelationshipValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.depth).toBe(2)
})
test('parses relationship path with types', () => {
const result = parser.parse('/related-to/src/auth.ts/types-uses,imports')
expect(result.dimension).toBe('relationship')
const value = result.value as RelationshipValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.relationshipTypes).toEqual(['uses', 'imports'])
})
test('parses relationship path with depth, types, and subpath', () => {
const result = parser.parse('/related-to/src/auth.ts/depth-3/types-uses,imports/helper.ts')
expect(result.dimension).toBe('relationship')
const value = result.value as RelationshipValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.depth).toBe(3)
expect(value.relationshipTypes).toEqual(['uses', 'imports'])
expect(result.subpath).toBe('helper.ts')
})
})
describe('Similarity Paths', () => {
test('parses similarity path without threshold', () => {
const result = parser.parse('/similar-to/src/auth.ts')
expect(result.dimension).toBe('similar')
const value = result.value as SimilarityValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.threshold).toBeUndefined()
})
test('parses similarity path with threshold', () => {
const result = parser.parse('/similar-to/src/auth.ts/threshold-0.85')
expect(result.dimension).toBe('similar')
const value = result.value as SimilarityValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.threshold).toBe(0.85)
})
test('parses similarity path with threshold and subpath', () => {
const result = parser.parse('/similar-to/src/auth.ts/threshold-0.7/login.ts')
expect(result.dimension).toBe('similar')
const value = result.value as SimilarityValue
expect(value.targetPath).toBe('src/auth.ts')
expect(value.threshold).toBe(0.7)
expect(result.subpath).toBe('login.ts')
})
})
describe('Tag Paths', () => {
test('parses tag path without subpath', () => {
const result = parser.parse('/by-tag/security')
expect(result.dimension).toBe('tag')
expect(result.value).toBe('security')
expect(result.subpath).toBeUndefined()
})
test('parses tag path with subpath', () => {
const result = parser.parse('/by-tag/security/auth.ts')
expect(result.dimension).toBe('tag')
expect(result.value).toBe('security')
expect(result.subpath).toBe('auth.ts')
})
test('parses tag with dashes', () => {
const result = parser.parse('/by-tag/code-review/file.ts')
expect(result.dimension).toBe('tag')
expect(result.value).toBe('code-review')
expect(result.subpath).toBe('file.ts')
})
})
describe('isSemanticPath', () => {
test('returns false for traditional paths', () => {
expect(parser.isSemanticPath('/src/auth.ts')).toBe(false)
expect(parser.isSemanticPath('/projects/main/file.ts')).toBe(false)
expect(parser.isSemanticPath('/')).toBe(false)
})
test('returns true for concept paths', () => {
expect(parser.isSemanticPath('/by-concept/auth')).toBe(true)
expect(parser.isSemanticPath('/by-concept/auth/file.ts')).toBe(true)
})
test('returns true for author paths', () => {
expect(parser.isSemanticPath('/by-author/alice')).toBe(true)
})
test('returns true for time paths', () => {
expect(parser.isSemanticPath('/as-of/2024-03-15')).toBe(true)
})
test('returns true for relationship paths', () => {
expect(parser.isSemanticPath('/related-to/src/auth.ts')).toBe(true)
})
test('returns true for similarity paths', () => {
expect(parser.isSemanticPath('/similar-to/src/auth.ts')).toBe(true)
})
test('returns true for tag paths', () => {
expect(parser.isSemanticPath('/by-tag/security')).toBe(true)
})
test('handles invalid input gracefully', () => {
expect(parser.isSemanticPath('')).toBe(false)
expect(parser.isSemanticPath(null as any)).toBe(false)
expect(parser.isSemanticPath(undefined as any)).toBe(false)
})
})
describe('getDimension', () => {
test('returns correct dimension for each path type', () => {
expect(parser.getDimension('/src/auth.ts')).toBe('traditional')
expect(parser.getDimension('/by-concept/auth')).toBe('concept')
expect(parser.getDimension('/by-author/alice')).toBe('author')
expect(parser.getDimension('/as-of/2024-03-15')).toBe('time')
expect(parser.getDimension('/related-to/src/file.ts')).toBe('relationship')
expect(parser.getDimension('/similar-to/src/file.ts')).toBe('similar')
expect(parser.getDimension('/by-tag/security')).toBe('tag')
})
})
describe('validate', () => {
test('validates correct parsed paths', () => {
const valid: ParsedSemanticPath = {
dimension: 'concept',
value: 'authentication'
}
expect(parser.validate(valid)).toBe(true)
})
test('validates time paths with Date objects', () => {
const valid: ParsedSemanticPath = {
dimension: 'time',
value: new Date('2024-03-15')
}
expect(parser.validate(valid)).toBe(true)
})
test('validates relationship paths', () => {
const valid: ParsedSemanticPath = {
dimension: 'relationship',
value: { targetPath: '/src/auth.ts', depth: 2 }
}
expect(parser.validate(valid)).toBe(true)
})
test('validates similarity paths', () => {
const valid: ParsedSemanticPath = {
dimension: 'similar',
value: { targetPath: '/src/auth.ts', threshold: 0.8 }
}
expect(parser.validate(valid)).toBe(true)
})
test('rejects invalid structures', () => {
expect(parser.validate(null as any)).toBe(false)
expect(parser.validate(undefined as any)).toBe(false)
expect(parser.validate({} as any)).toBe(false)
})
test('rejects missing dimension', () => {
expect(parser.validate({ value: 'test' } as any)).toBe(false)
})
test('rejects missing value', () => {
expect(parser.validate({ dimension: 'concept' } as any)).toBe(false)
})
test('rejects invalid time values', () => {
const invalid: ParsedSemanticPath = {
dimension: 'time',
value: 'not-a-date' as any
}
expect(parser.validate(invalid)).toBe(false)
})
test('rejects invalid relationship values', () => {
const invalid: ParsedSemanticPath = {
dimension: 'relationship',
value: { targetPath: '' } as any
}
expect(parser.validate(invalid)).toBe(false)
})
})
describe('Error Handling', () => {
test('throws on null path', () => {
expect(() => parser.parse(null as any)).toThrow('Path must be a non-empty string')
})
test('throws on undefined path', () => {
expect(() => parser.parse(undefined as any)).toThrow('Path must be a non-empty string')
})
test('throws on empty string', () => {
expect(() => parser.parse('')).toThrow('Path must be a non-empty string')
})
test('throws on non-string path', () => {
expect(() => parser.parse(123 as any)).toThrow('Path must be a non-empty string')
})
})
describe('Edge Cases', () => {
test('handles paths with special characters in concepts', () => {
const result = parser.parse('/by-concept/oauth-2.0/auth.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('oauth-2.0')
})
test('handles paths with underscores', () => {
const result = parser.parse('/by-concept/user_authentication/login.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('user_authentication')
})
test('handles deeply nested subpaths', () => {
const result = parser.parse('/by-concept/auth/src/lib/utils/helpers/validators/email.ts')
expect(result.dimension).toBe('concept')
expect(result.value).toBe('auth')
expect(result.subpath).toBe('src/lib/utils/helpers/validators/email.ts')
})
test('handles paths with dots', () => {
const result = parser.parse('/by-author/alice/v2.0.0/file.ts')
expect(result.dimension).toBe('author')
expect(result.value).toBe('alice')
expect(result.subpath).toBe('v2.0.0/file.ts')
})
})
})

View file

@ -232,48 +232,6 @@ describe('VirtualFileSystem - Comprehensive Test Suite', () => {
})
})
describe('Knowledge Layer', () => {
it('should enable and use Knowledge Layer features', async () => {
// Enable Knowledge Layer
await vfs.enableKnowledgeLayer()
// Write a file to trigger Knowledge Layer processing
await vfs.writeFile('/knowledge.txt', 'This is a test of the knowledge system')
// Wait for background processing
await new Promise(resolve => setTimeout(resolve, 100))
// Test Knowledge Layer methods (added dynamically)
if ('getHistory' in vfs) {
const history = await (vfs as any).getHistory('/knowledge.txt')
expect(history).toBeDefined()
}
if ('getVersions' in vfs) {
const versions = await (vfs as any).getVersions('/knowledge.txt')
expect(versions).toBeDefined()
}
if ('createEntity' in vfs) {
const entity = await (vfs as any).createEntity({
name: 'TestEntity',
type: 'character',
description: 'A test character'
})
expect(entity).toBeDefined()
}
if ('createConcept' in vfs) {
const concept = await (vfs as any).createConcept({
name: 'TestConcept',
type: 'idea',
domain: 'testing'
})
expect(concept).toBeDefined()
}
})
})
describe('Import/Export', () => {
it('should import files from local filesystem', async () => {
// Create a temp file
@ -380,20 +338,4 @@ describe('GitBridge Integration', () => {
await brain?.close()
})
it('should export relationships and history (FIXED - now queries real data)', async () => {
// Enable Knowledge Layer for history
await vfs.enableKnowledgeLayer()
// Create files with relationships
await vfs.writeFile('/src/index.js', 'export default {}')
await vfs.writeFile('/src/utils.js', 'export function util() {}')
await vfs.addRelationship('/src/index.js', '/src/utils.js', VerbType.Uses)
// GitBridge export would now include real relationships and events
const gitBridge = (vfs as any).gitBridge
if (gitBridge) {
// The export methods now query actual VFS data instead of returning empty arrays
expect(gitBridge).toBeDefined()
}
})
})