/** * Integration tests for Memory Enhancements (v5.11.0) * * End-to-end tests verifying: * - Container detection works correctly * - Memory limits are applied correctly * - getMemoryStats() provides accurate information * - All features work together seamlessly */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { Brainy } from '../../src/brainy.js' import { ValidationConfig, resetLimitWarningCache } from '../../src/utils/paramValidation.js' import { mkdtempSync, rmSync } from 'fs' import { tmpdir } from 'os' import { join } from 'path' describe('Memory Enhancements Integration (v5.11.0)', () => { let testDir: string let originalEnv: Record beforeEach(() => { testDir = mkdtempSync(join(tmpdir(), 'brainy-memory-integration-')) originalEnv = { CLOUD_RUN_MEMORY: process.env.CLOUD_RUN_MEMORY, MEMORY_LIMIT: process.env.MEMORY_LIMIT } // ValidationConfig is a process-wide singleton that only rebuilds when a brain // passes maxQueryLimit/reservedQueryMemory (brainy.ts init reconfigure). Tests // here rely on auto-detection (container/free-memory tiers), which read live // process state when the singleton is first constructed. Reset both the config // singleton and the limit-warning dedup before every test so each one observes // the env it sets — without this, basis/maxLimit bleed from earlier tests and // results become order-dependent. ValidationConfig.reset() resetLimitWarningCache() }) afterEach(() => { rmSync(testDir, { recursive: true, force: true }) Object.keys(originalEnv).forEach(key => { if (originalEnv[key] === undefined) { delete process.env[key] } else { process.env[key] = originalEnv[key] } }) // Leave the singleton clean for any other integration file sharing this process. ValidationConfig.reset() resetLimitWarningCache() }) describe('Production Workflow: Cloud Run Deployment', () => { it('should detect 4GB Cloud Run container and set optimal limits', async () => { process.env.CLOUD_RUN_MEMORY = '4Gi' const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() const stats = brain.getMemoryStats() // Verify container detected expect(stats.memory.containerLimit).toBe(4 * 1024 * 1024 * 1024) // Verify optimal limits. // containerMemory tier reserves 25% of the container for queries, then // divides by 25 KB/result (MAX_LIMIT_KB_PER_RESULT) and floors to 1k steps: // 4GiB * 0.25 = 1 GiB; floor(1 GiB / (25 KB)) = 40_960 -> 40_000. expect(stats.limits.basis).toBe('containerMemory') expect(stats.limits.maxQueryLimit).toBe(40000) // Verify can query with these limits for (let i = 0; i < 100; i++) { await brain.add({ type: 'document', data: `Entity ${i}` }) } const results = await brain.find({ limit: 10000 }) // Should not throw expect(results.length).toBe(100) await brain.close() }) it('should allow manual override for power users in containers', async () => { process.env.CLOUD_RUN_MEMORY = '4Gi' const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, maxQueryLimit: 50000, // Override auto-detection silent: true }) await brain.init() const stats = brain.getMemoryStats() // Container detected but override used expect(stats.memory.containerLimit).toBe(4 * 1024 * 1024 * 1024) expect(stats.limits.basis).toBe('override') expect(stats.limits.maxQueryLimit).toBe(50000) await brain.close() }) it('should use reservedQueryMemory for fine-grained control', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, reservedQueryMemory: 2 * 1024 * 1024 * 1024, // Reserve 2GB silent: true }) await brain.init() const stats = brain.getMemoryStats() // reservedMemory tier divides the reserved bytes by 25 KB/result and floors // to 1k steps: floor(2 GiB / (25 KB)) = 81_920 -> 81_000. expect(stats.limits.basis).toBe('reservedMemory') expect(stats.limits.maxQueryLimit).toBe(81000) await brain.close() }) }) describe('Memory Stats API', () => { it('should provide actionable recommendations', async () => { process.env.CLOUD_RUN_MEMORY = '4Gi' const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() const stats = brain.getMemoryStats() // Should have recommendations array expect(stats.recommendations).toBeDefined() expect(Array.isArray(stats.recommendations)).toBe(true) // Should not have error recommendations for well-configured system const errorRecommendations = stats.recommendations?.filter(r => r.toLowerCase().includes('error') || r.toLowerCase().includes('warning') ) expect(errorRecommendations?.length || 0).toBe(0) await brain.close() }) it('should help debug low limits in production', async () => { // Simulate production issue: container detected but low free memory process.env.CLOUD_RUN_MEMORY = '4Gi' const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() const stats = brain.getMemoryStats() // User can see why limits are what they are expect(stats.limits.basis).toBeDefined() expect(stats.memory.containerLimit).toBeDefined() expect(stats.config).toBeDefined() // Can debug: "Why is my limit what it is?" // Answer: basis='containerMemory', container=4GB, 4GB * 0.25 = 1GB / 25KB -> 40k await brain.close() }) }) describe('Zero-Config Behavior', () => { it('should work optimally with no configuration', async () => { process.env.CLOUD_RUN_MEMORY = '4Gi' const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() // Zero config, optimal behavior const stats = brain.getMemoryStats() // Same calc as the 4GiB container test above: 25% of 4 GiB / 25 KB -> 40_000. expect(stats.limits.maxQueryLimit).toBe(40000) expect(stats.limits.basis).toBe('containerMemory') // Should work without issues for (let i = 0; i < 100; i++) { await brain.add({ type: 'document', data: `Test ${i}` }) } const results = await brain.find({ limit: 100 }) expect(results.length).toBe(100) await brain.close() }) it('should work on bare metal without containers', async () => { // No container env vars const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() const stats = brain.getMemoryStats() expect(stats.memory.containerLimit).toBeNull() expect(stats.limits.basis).toBe('freeMemory') expect(stats.limits.maxQueryLimit).toBeGreaterThan(0) await brain.close() }) }) describe('Backward Compatibility', () => { it('should not break existing code', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() // Existing APIs work await brain.add({ type: 'document', data: 'Test' }) const results = await brain.find({ limit: 10 }) expect(results.length).toBe(1) await brain.close() }) }) })