brainy/tests/unit/brainy/get.test.ts
David Snelling dc94af3a6a test(8.0): get() resolves null for absent custom ids instead of throwing
Follow-up to accepting application-supplied ids (36b7216): get('not-a-uuid')
and long ids are now valid lookups that miss → null, not "Invalid UUID format"
throws. Empty / null / undefined still throw (structurally invalid input).
Aligns the unit suite with the accept-any-id behaviour; the v5.1.0 "stricter
UUID validation" rule these cases encoded is superseded.
2026-06-16 10:47:10 -07:00

419 lines
No EOL
12 KiB
TypeScript

/**
* Unit tests for Brainy.get() method
* Tests all aspects of retrieving entities from the neural database
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../../src/brainy'
import {
createAddParams,
generateTestVector,
createTestConfig,
} from '../../helpers/test-factory'
import {
assertCompletesWithin,
} from '../../helpers/test-assertions'
describe('Brainy.get()', () => {
let brain: Brainy
beforeEach(async () => {
brain = new Brainy(createTestConfig())
await brain.init()
})
afterEach(async () => {
await brain.close()
})
describe('success paths', () => {
it('should retrieve an existing entity by ID', async () => {
// Arrange
const params = createAddParams({
data: 'Test entity for retrieval',
type: 'document',
metadata: { title: 'Test Doc', version: 1 }
})
const id = await brain.add(params)
// Act
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.id).toBe(id)
expect(entity!.type).toBe('document')
expect(entity!.metadata).toBeDefined()
})
it('should retrieve entity with correct vector', async () => {
// Arrange
const vector = generateTestVector()
const params = createAddParams({
vector,
type: 'thing',
metadata: { vectorized: true }
})
const id = await brain.add(params)
// Act - v5.11.1: Need includeVectors to get vector data
const entity = await brain.get(id, { includeVectors: true })
// Assert
expect(entity).not.toBeNull()
expect(entity!.vector).toEqual(vector)
})
it('should retrieve entity with metadata intact', async () => {
// Arrange
const metadata = {
name: 'Test Item',
count: 42,
tags: ['test', 'unit'],
nested: { level: 1, data: 'value' }
}
const params = createAddParams({
data: 'Metadata test',
type: 'thing',
metadata
})
const id = await brain.add(params)
// Act
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
// Check specific fields since metadata might be modified
expect(entity!.metadata.name).toBe(metadata.name)
expect(entity!.metadata.count).toBe(metadata.count)
expect(entity!.metadata.tags).toEqual(metadata.tags)
})
it('should retrieve entity with timestamps', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Timestamp test',
type: 'thing'
}))
// Act
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.createdAt).toBeDefined()
expect(entity!.createdAt).toBeTypeOf('number')
expect(entity!.createdAt).toBeGreaterThan(0)
})
it('should retrieve entity with service namespace', async () => {
// Arrange
const service = 'tenant-456'
const id = await brain.add(createAddParams({
data: 'Service test',
type: 'thing',
service
}))
// Act
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.service).toBe(service)
})
it('should retrieve multiple different entities correctly', async () => {
// Arrange
const ids: string[] = []
for (let i = 0; i < 5; i++) {
const id = await brain.add(createAddParams({
data: `Entity ${i}`,
type: 'thing',
metadata: { index: i }
}))
ids.push(id)
}
// Act & Assert
for (let i = 0; i < ids.length; i++) {
const entity = await brain.get(ids[i])
expect(entity).not.toBeNull()
expect(entity!.id).toBe(ids[i])
expect(entity!.metadata.index).toBe(i)
}
})
})
describe('error paths', () => {
it('should return null for non-existent ID', async () => {
// v5.1.0: Use valid UUID format
const fakeId = '00000000-0000-0000-0000-999999999999'
// Act
const entity = await brain.get(fakeId)
// Assert
expect(entity).toBeNull()
})
it('should handle invalid ID formats gracefully', async () => {
// 8.0: any non-empty string is a valid custom id (sharding hashes it), so
// a lookup that misses resolves to null — there is no "invalid format".
const missingIds = [
'not-a-uuid', // valid custom id, simply absent
'12345' // valid custom id, simply absent
]
for (const id of missingIds) {
await expect(brain.get(id)).resolves.toBeNull()
}
// Structurally-invalid input (empty / null / undefined) still throws.
await expect(brain.get('')).rejects.toThrow()
await expect(brain.get(null as any)).rejects.toThrow()
await expect(brain.get(undefined as any)).rejects.toThrow()
})
it('should handle deleted entities', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'To be deleted',
type: 'thing'
}))
await brain.remove(id)
// Act
const entity = await brain.get(id)
// Assert
expect(entity).toBeNull()
})
it('should handle concurrent get requests for same ID', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Concurrent test',
type: 'thing',
metadata: { concurrent: true }
}))
// Act - Make 10 concurrent requests
const promises = Array(10).fill(null).map(() => brain.get(id))
const results = await Promise.all(promises)
// Assert - All should return the same entity
expect(results).toHaveLength(10)
results.forEach(entity => {
expect(entity).not.toBeNull()
expect(entity!.id).toBe(id)
expect(entity!.metadata.concurrent).toBe(true)
})
})
})
describe('edge cases', () => {
it('should handle very long IDs', async () => {
// 8.0: a long string is a valid custom id; a miss resolves to null.
const longId = 'x'.repeat(1000)
await expect(brain.get(longId)).resolves.toBeNull()
})
it('should handle special characters in IDs', async () => {
// v5.1.0: Use valid UUID format
const specialId = '00000000-0000-0000-0000-000000000004'
const params = createAddParams({
id: specialId,
data: 'Special ID test',
type: 'thing'
})
await brain.add(params)
// Act
const entity = await brain.get(specialId)
// Assert
expect(entity).not.toBeNull()
expect(entity!.id).toBe(specialId)
})
it('should handle unicode characters in IDs', async () => {
// Arrange
const unicodeId = '00000000-0000-0000-0000-000000000003'
const params = createAddParams({
id: unicodeId,
data: 'Unicode ID test',
type: 'thing'
})
await brain.add(params)
// Act
const entity = await brain.get(unicodeId)
// Assert
expect(entity).not.toBeNull()
expect(entity!.id).toBe(unicodeId)
})
it('should handle immediately getting after add', async () => {
// Arrange & Act
const id = await brain.add(createAddParams({
data: 'Immediate get test',
type: 'thing'
}))
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.id).toBe(id)
})
it('should get entity with very large metadata', async () => {
// Arrange
const largeMetadata = {
bigArray: new Array(1000).fill('item'),
bigObject: Object.fromEntries(
Array.from({ length: 100 }, (_, i) => [`key${i}`, `value${i}`])
),
deepNesting: Array(10).fill(null).reduce(
(acc) => ({ nested: acc }),
{ value: 'deep' }
)
}
const id = await brain.add(createAddParams({
data: 'Large metadata',
type: 'thing',
metadata: largeMetadata
}))
// Act
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.metadata.bigArray).toHaveLength(1000)
expect(Object.keys(entity!.metadata.bigObject)).toHaveLength(100)
})
})
describe('performance', () => {
it('should retrieve entities quickly', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Performance test',
type: 'thing'
}))
// Act & Assert
await assertCompletesWithin(
() => brain.get(id),
50, // Should complete within 50ms
'Get operation'
)
})
it('should handle batch gets efficiently', async () => {
// Arrange - Add 100 entities
const ids: string[] = []
for (let i = 0; i < 100; i++) {
const id = await brain.add(createAddParams({
data: `Batch entity ${i}`,
type: 'thing'
}))
ids.push(id)
}
// Act - Get all entities
const start = performance.now()
const entities = await Promise.all(ids.map(id => brain.get(id)))
const duration = performance.now() - start
// Assert
expect(entities).toHaveLength(100)
entities.forEach(entity => {
expect(entity).not.toBeNull()
})
const opsPerSecond = (100 / duration) * 1000
expect(opsPerSecond).toBeGreaterThan(500) // At least 500 gets/second
})
it('should retrieve entities quickly on repeated access', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Cache test',
type: 'thing',
metadata: { cached: true }
}))
// Act - First get (may hit storage)
const entity1 = await brain.get(id)
// Act - Second get (may benefit from caching)
const start = performance.now()
const entity2 = await brain.get(id)
const duration = performance.now() - start
// Assert - Entities should be equivalent (not necessarily same instance)
expect(entity1).not.toBeNull()
expect(entity2).not.toBeNull()
expect(entity1!.id).toBe(entity2!.id)
expect(entity1!.type).toBe(entity2!.type)
expect(entity1!.metadata.cached).toBe(true)
expect(entity2!.metadata.cached).toBe(true)
expect(duration).toBeLessThan(10) // Should be fast
})
})
describe('consistency', () => {
it('should return consistent data across multiple gets', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Consistency test',
type: 'thing',
metadata: { version: 1, stable: true }
}))
// Act - Get the same entity 5 times
const entities: any[] = []
for (let i = 0; i < 5; i++) {
const entity = await brain.get(id)
entities.push(entity)
}
// Assert - All should have identical content (not necessarily same instance)
const first = entities[0]
expect(first).not.toBeNull()
entities.forEach(entity => {
expect(entity).not.toBeNull()
expect(entity.id).toBe(first.id)
expect(entity.type).toBe(first.type)
expect(entity.metadata.version).toBe(1)
expect(entity.metadata.stable).toBe(true)
expect(entity.createdAt).toBe(first.createdAt)
})
})
it('should reflect updates immediately', async () => {
// Arrange
const id = await brain.add(createAddParams({
data: 'Update test',
type: 'thing',
metadata: { version: 1 }
}))
// Act - Update and immediately get
await brain.update({
id,
metadata: { version: 2 },
merge: false
})
const entity = await brain.get(id)
// Assert
expect(entity).not.toBeNull()
expect(entity!.metadata.version).toBe(2)
})
})
})