createRelateParams / createTestRelation / createKnowledgeGraphTestData cast
PascalCase strings ('RelatedTo', 'DependsOn', 'Contains') `as VerbType` — the
cast hid the mismatch from TS, but 8.0's verb-type validation rejects them at
runtime ("invalid VerbType"). Corrected to the real enum values (relatedTo,
dependsOn, contains), matching the already-correct social-network helpers.
Unblocks find-unified-integration setup (59 failing → 40 passing).
488 lines
No EOL
12 KiB
TypeScript
488 lines
No EOL
12 KiB
TypeScript
/**
|
|
* Test Factory - Comprehensive test data generation and utilities
|
|
* Provides consistent, realistic test data for all Brainy tests
|
|
*/
|
|
|
|
import { v4 as uuidv4 } from '../../src/universal/uuid'
|
|
import type { NounType, VerbType } from '../../src/types/graphTypes'
|
|
import type {
|
|
Entity,
|
|
Relation,
|
|
Result,
|
|
AddParams,
|
|
UpdateParams,
|
|
RelateParams,
|
|
FindParams,
|
|
BrainyConfig,
|
|
} from '../../src/types/brainy.types'
|
|
import type { Vector } from '../../src/coreTypes'
|
|
|
|
/**
|
|
* Test Data Generators
|
|
*/
|
|
|
|
// Generate a unique test ID
|
|
export function generateTestId(prefix = 'test'): string {
|
|
return `${prefix}_${uuidv4().slice(0, 8)}_${Date.now()}`
|
|
}
|
|
|
|
// Generate test vector (384-dimensional vector matching all-MiniLM-L6-v2)
|
|
export function generateTestVector(dimension = 384): Vector {
|
|
// Generate a deterministic but varied embedding
|
|
const vector = new Array(dimension)
|
|
for (let i = 0; i < dimension; i++) {
|
|
// Create varied but deterministic values
|
|
vector[i] = Math.sin(i * 0.1) * 0.5 + Math.cos(i * 0.05) * 0.3
|
|
}
|
|
// Normalize to unit vector
|
|
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0))
|
|
return vector.map(val => val / magnitude)
|
|
}
|
|
|
|
// Generate realistic metadata
|
|
export function generateTestMetadata(overrides: any = {}): any {
|
|
return {
|
|
name: `Test Item ${generateTestId()}`,
|
|
description: 'Test description',
|
|
tags: ['test', 'automated'],
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
version: 1,
|
|
source: 'test-factory',
|
|
confidence: 0.95,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate a test entity
|
|
export function createTestEntity(overrides: Partial<Entity> = {}): Entity {
|
|
const id = overrides.id || generateTestId('entity')
|
|
const now = Date.now()
|
|
|
|
return {
|
|
id,
|
|
type: 'person' as NounType,
|
|
vector: generateTestVector(),
|
|
metadata: generateTestMetadata(),
|
|
service: 'test',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate multiple test entities
|
|
export function createTestEntities(count: number, baseOverrides: Partial<Entity> = {}): Entity[] {
|
|
return Array.from({ length: count }, (_, i) =>
|
|
createTestEntity({
|
|
...baseOverrides,
|
|
metadata: {
|
|
...generateTestMetadata(),
|
|
name: `Test Entity ${i + 1}`,
|
|
index: i,
|
|
},
|
|
})
|
|
)
|
|
}
|
|
|
|
// Generate a test relation
|
|
export function createTestRelation(overrides: Partial<Relation> = {}): Relation {
|
|
const id = overrides.id || generateTestId('relation')
|
|
const now = Date.now()
|
|
|
|
return {
|
|
id,
|
|
from: overrides.from || generateTestId('from'),
|
|
to: overrides.to || generateTestId('to'),
|
|
type: (overrides.type || 'RelatedTo') as VerbType,
|
|
weight: 1.0,
|
|
metadata: generateTestMetadata(),
|
|
service: 'test',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate multiple test relations
|
|
export function createTestRelations(count: number, baseOverrides: Partial<Relation> = {}): Relation[] {
|
|
return Array.from({ length: count }, (_, i) =>
|
|
createTestRelation({
|
|
...baseOverrides,
|
|
metadata: {
|
|
...generateTestMetadata(),
|
|
index: i,
|
|
},
|
|
})
|
|
)
|
|
}
|
|
|
|
// Generate a test result (entity with score)
|
|
export function createTestResult(
|
|
score: number = 0.95,
|
|
entityOverrides: Partial<Entity> = {}
|
|
): Result {
|
|
return {
|
|
id: entityOverrides.id || generateTestId('result'),
|
|
score,
|
|
entity: createTestEntity(entityOverrides),
|
|
}
|
|
}
|
|
|
|
// Generate add parameters
|
|
export function createAddParams(overrides: Partial<AddParams> = {}): AddParams {
|
|
return {
|
|
data: 'Test content for embedding',
|
|
type: 'thing' as NounType,
|
|
metadata: generateTestMetadata(),
|
|
service: 'test',
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate update parameters
|
|
export function createUpdateParams(id: string, overrides: Partial<UpdateParams> = {}): UpdateParams {
|
|
return {
|
|
id,
|
|
metadata: { updated: true },
|
|
merge: true,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate relate parameters
|
|
export function createRelateParams(from: string, to: string, overrides: Partial<RelateParams> = {}): RelateParams {
|
|
return {
|
|
from,
|
|
to,
|
|
type: 'relatedTo' as VerbType,
|
|
weight: 1.0,
|
|
metadata: generateTestMetadata(),
|
|
service: 'test',
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate find parameters
|
|
export function createFindParams(overrides: Partial<FindParams> = {}): FindParams {
|
|
return {
|
|
query: 'test query',
|
|
limit: 10,
|
|
offset: 0,
|
|
explain: false,
|
|
includeRelations: false,
|
|
service: 'test',
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
// Generate test configuration. Brainy 8.0 defaults `requireSubtype: true`;
|
|
// tests opt out unless they specifically exercise subtype enforcement.
|
|
export function createTestConfig(overrides: Partial<BrainyConfig> = {}): BrainyConfig {
|
|
return {
|
|
requireSubtype: false,
|
|
storage: { type: 'memory' },
|
|
model: { type: 'fast' },
|
|
index: {
|
|
m: 16,
|
|
efConstruction: 200,
|
|
efSearch: 50,
|
|
},
|
|
cache: { maxSize: 1000, ttl: 3600 },
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Data Sets - Pre-built realistic scenarios
|
|
*/
|
|
|
|
// Create a social network graph
|
|
export function createSocialNetworkTestData() {
|
|
const alice = createTestEntity({
|
|
id: 'alice',
|
|
type: 'person' as NounType,
|
|
metadata: { name: 'Alice', age: 30 }
|
|
})
|
|
const bob = createTestEntity({
|
|
id: 'bob',
|
|
type: 'person' as NounType,
|
|
metadata: { name: 'Bob', age: 25 }
|
|
})
|
|
const charlie = createTestEntity({
|
|
id: 'charlie',
|
|
type: 'person' as NounType,
|
|
metadata: { name: 'Charlie', age: 35 }
|
|
})
|
|
const diana = createTestEntity({
|
|
id: 'diana',
|
|
type: 'person' as NounType,
|
|
metadata: { name: 'Diana', age: 28 }
|
|
})
|
|
|
|
const entities = [alice, bob, charlie, diana]
|
|
|
|
const relations = [
|
|
createTestRelation({ from: 'alice', to: 'bob', type: 'friendOf' as VerbType }),
|
|
createTestRelation({ from: 'alice', to: 'charlie', type: 'friendOf' as VerbType }),
|
|
createTestRelation({ from: 'bob', to: 'charlie', type: 'worksWith' as VerbType }),
|
|
createTestRelation({ from: 'charlie', to: 'diana', type: 'mentors' as VerbType }),
|
|
createTestRelation({ from: 'alice', to: 'diana', type: 'likes' as VerbType }),
|
|
]
|
|
|
|
return { entities, relations }
|
|
}
|
|
|
|
// Create a knowledge graph
|
|
export function createKnowledgeGraphTestData() {
|
|
const entities = [
|
|
createTestEntity({
|
|
id: 'earth',
|
|
type: 'location' as NounType,
|
|
metadata: { name: 'Earth', type: 'planet' }
|
|
}),
|
|
createTestEntity({
|
|
id: 'sun',
|
|
type: 'thing' as NounType,
|
|
metadata: { name: 'Sun', type: 'star' }
|
|
}),
|
|
createTestEntity({
|
|
id: 'moon',
|
|
type: 'thing' as NounType,
|
|
metadata: { name: 'Moon', type: 'satellite' }
|
|
}),
|
|
createTestEntity({
|
|
id: 'mars',
|
|
type: 'location' as NounType,
|
|
metadata: { name: 'Mars', type: 'planet' }
|
|
}),
|
|
]
|
|
|
|
const relations = [
|
|
createTestRelation({ from: 'earth', to: 'sun', type: 'dependsOn' as VerbType }),
|
|
createTestRelation({ from: 'moon', to: 'earth', type: 'dependsOn' as VerbType }),
|
|
createTestRelation({ from: 'mars', to: 'sun', type: 'dependsOn' as VerbType }),
|
|
createTestRelation({ from: 'earth', to: 'moon', type: 'contains' as VerbType }),
|
|
]
|
|
|
|
return { entities, relations }
|
|
}
|
|
|
|
// Create large dataset for performance testing
|
|
export function createLargeTestDataset(entityCount = 1000, relationCount = 5000) {
|
|
const entities = createTestEntities(entityCount)
|
|
const relations: Relation[] = []
|
|
|
|
const verbTypes = ['relatedTo', 'friendOf', 'worksWith', 'creates', 'owns'] as VerbType[]
|
|
|
|
// Create random relations between entities
|
|
for (let i = 0; i < relationCount; i++) {
|
|
const fromIdx = Math.floor(Math.random() * entityCount)
|
|
const toIdx = Math.floor(Math.random() * entityCount)
|
|
if (fromIdx !== toIdx) {
|
|
relations.push(
|
|
createTestRelation({
|
|
from: entities[fromIdx].id,
|
|
to: entities[toIdx].id,
|
|
type: verbTypes[Math.floor(Math.random() * verbTypes.length)],
|
|
})
|
|
)
|
|
}
|
|
}
|
|
|
|
return { entities, relations }
|
|
}
|
|
|
|
/**
|
|
* Test Assertion Helpers
|
|
*/
|
|
|
|
export function assertEntity(entity: any): asserts entity is Entity {
|
|
if (!entity || typeof entity !== 'object') {
|
|
throw new Error('Entity must be an object')
|
|
}
|
|
if (!entity.id || typeof entity.id !== 'string') {
|
|
throw new Error('Entity must have a string id')
|
|
}
|
|
if (!entity.type || typeof entity.type !== 'string') {
|
|
throw new Error('Entity must have a string type')
|
|
}
|
|
if (!entity.vector || !Array.isArray(entity.vector)) {
|
|
throw new Error('Entity must have an array vector')
|
|
}
|
|
if (typeof entity.createdAt !== 'number') {
|
|
throw new Error('Entity must have a numeric createdAt timestamp')
|
|
}
|
|
}
|
|
|
|
export function assertRelation(relation: any): asserts relation is Relation {
|
|
if (!relation || typeof relation !== 'object') {
|
|
throw new Error('Relation must be an object')
|
|
}
|
|
if (!relation.id || typeof relation.id !== 'string') {
|
|
throw new Error('Relation must have a string id')
|
|
}
|
|
if (!relation.from || typeof relation.from !== 'string') {
|
|
throw new Error('Relation must have a string from')
|
|
}
|
|
if (!relation.to || typeof relation.to !== 'string') {
|
|
throw new Error('Relation must have a string to')
|
|
}
|
|
if (!relation.type || typeof relation.type !== 'string') {
|
|
throw new Error('Relation must have a string type')
|
|
}
|
|
if (typeof relation.createdAt !== 'number') {
|
|
throw new Error('Relation must have a numeric createdAt timestamp')
|
|
}
|
|
}
|
|
|
|
export function assertResult(result: any): asserts result is Result {
|
|
if (!result || typeof result !== 'object') {
|
|
throw new Error('Result must be an object')
|
|
}
|
|
if (!result.id || typeof result.id !== 'string') {
|
|
throw new Error('Result must have a string id')
|
|
}
|
|
if (typeof result.score !== 'number') {
|
|
throw new Error('Result must have a numeric score')
|
|
}
|
|
assertEntity(result.entity)
|
|
}
|
|
|
|
/**
|
|
* Test Cleanup Helpers
|
|
*/
|
|
|
|
export class TestCleanup {
|
|
private cleanupFunctions: Array<() => Promise<void>> = []
|
|
|
|
register(fn: () => Promise<void>) {
|
|
this.cleanupFunctions.push(fn)
|
|
}
|
|
|
|
async cleanup() {
|
|
for (const fn of this.cleanupFunctions.reverse()) {
|
|
try {
|
|
await fn()
|
|
} catch (error) {
|
|
console.error('Cleanup error:', error)
|
|
}
|
|
}
|
|
this.cleanupFunctions = []
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Timing Helpers
|
|
*/
|
|
|
|
export async function measureExecutionTime<T>(
|
|
fn: () => Promise<T>,
|
|
label?: string
|
|
): Promise<{ result: T; duration: number }> {
|
|
const start = performance.now()
|
|
const result = await fn()
|
|
const duration = performance.now() - start
|
|
|
|
if (label) {
|
|
console.log(`${label}: ${duration.toFixed(2)}ms`)
|
|
}
|
|
|
|
return { result, duration }
|
|
}
|
|
|
|
/**
|
|
* Test Delay Helper
|
|
*/
|
|
|
|
export function delay(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
}
|
|
|
|
/**
|
|
* Mock Storage Helper
|
|
*/
|
|
|
|
export function createMockStorage() {
|
|
const store = new Map<string, any>()
|
|
|
|
return {
|
|
get: async (key: string) => store.get(key),
|
|
set: async (key: string, value: any) => { store.set(key, value); return true },
|
|
delete: async (key: string) => store.delete(key),
|
|
has: async (key: string) => store.has(key),
|
|
clear: async () => store.clear(),
|
|
size: () => store.size,
|
|
entries: () => Array.from(store.entries()),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Error Generators
|
|
*/
|
|
|
|
export function createTestError(message = 'Test error', code = 'TEST_ERROR') {
|
|
const error = new Error(message) as any
|
|
error.code = code
|
|
return error
|
|
}
|
|
|
|
export function createNetworkError() {
|
|
return createTestError('Network request failed', 'NETWORK_ERROR')
|
|
}
|
|
|
|
export function createTimeoutError() {
|
|
return createTestError('Operation timed out', 'TIMEOUT_ERROR')
|
|
}
|
|
|
|
export function createValidationError(field: string) {
|
|
return createTestError(`Validation failed for field: ${field}`, 'VALIDATION_ERROR')
|
|
}
|
|
|
|
// Export everything as a namespace for convenience
|
|
export const TestFactory = {
|
|
// IDs
|
|
generateTestId,
|
|
|
|
// Core data
|
|
generateTestVector,
|
|
generateTestMetadata,
|
|
createTestEntity,
|
|
createTestEntities,
|
|
createTestRelation,
|
|
createTestRelations,
|
|
createTestResult,
|
|
|
|
// Parameters
|
|
createAddParams,
|
|
createUpdateParams,
|
|
createRelateParams,
|
|
createFindParams,
|
|
|
|
// Config
|
|
createTestConfig,
|
|
|
|
// Datasets
|
|
createSocialNetworkTestData,
|
|
createKnowledgeGraphTestData,
|
|
createLargeTestDataset,
|
|
|
|
// Assertions
|
|
assertEntity,
|
|
assertRelation,
|
|
assertResult,
|
|
|
|
// Utilities
|
|
TestCleanup,
|
|
measureExecutionTime,
|
|
delay,
|
|
createMockStorage,
|
|
|
|
// Errors
|
|
createTestError,
|
|
createNetworkError,
|
|
createTimeoutError,
|
|
createValidationError,
|
|
}
|
|
|
|
export default TestFactory |