@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
147 lines (146 loc) ⢠7.42 kB
JavaScript
/**
* Test cache invalidation on data updates
* Verifies that cache is properly cleared when data changes
*/
import { IntelligentQueryEngine } from './IntelligentQueryEngine.js';
import { OptimizelyAdapter } from './adapters/OptimizelyAdapter.js';
import Database from 'better-sqlite3';
console.log('Testing Cache Invalidation on Data Updates...\n');
async function setupDatabase(db) {
// Create tables
db.exec(`
CREATE TABLE IF NOT EXISTS flags (
id TEXT PRIMARY KEY,
key TEXT NOT NULL,
name TEXT,
enabled INTEGER DEFAULT 1,
project_id TEXT
);
CREATE TABLE IF NOT EXISTS experiments (
id TEXT PRIMARY KEY,
key TEXT NOT NULL,
name TEXT,
status TEXT,
project_id TEXT
);
`);
// Insert initial data
const insertFlag = db.prepare('INSERT INTO flags (id, key, name, enabled, project_id) VALUES (?, ?, ?, ?, ?)');
insertFlag.run('flag1', 'feature_a', 'Feature A', 1, 'proj1');
insertFlag.run('flag2', 'feature_b', 'Feature B', 0, 'proj1');
insertFlag.run('flag3', 'feature_c', 'Feature C', 1, 'proj1');
const insertExp = db.prepare('INSERT INTO experiments (id, key, name, status, project_id) VALUES (?, ?, ?, ?, ?)');
insertExp.run('exp1', 'test_a', 'Test A', 'running', 'proj1');
insertExp.run('exp2', 'test_b', 'Test B', 'paused', 'proj1');
}
async function testCacheInvalidation() {
try {
// Initialize database
const db = new Database(':memory:');
await setupDatabase(db);
// Create engine with cache
const engine = new IntelligentQueryEngine({
cache: {
enabled: true,
defaultTTL: 300000, // 5 minutes
}
});
// Register adapter
const adapter = new OptimizelyAdapter({ database: db });
engine.registerAdapter(adapter);
// Test queries
const flagQuery = {
find: 'flags',
select: ['key', 'name', 'enabled'],
where: [{ field: 'enabled', operator: '=', value: 1 }],
};
const experimentQuery = {
find: 'experiments',
select: ['key', 'status'],
groupBy: ['status'],
aggregations: [{ field: 'id', function: 'COUNT', alias: 'count' }],
};
console.log('=== Test 1: Initial Query & Cache ===');
console.log('1.1 First query (should NOT be cached)');
let result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached} | Enabled flags: ${result.data.length}`);
console.log(` Flags:`, result.data.map(f => f.key).join(', '));
console.log('\n1.2 Second query (should BE cached)');
result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached} | Enabled flags: ${result.data.length}`);
console.log('\n=== Test 2: Update Data Without Invalidation ===');
console.log('2.1 Updating feature_b to enabled=1');
db.prepare('UPDATE flags SET enabled = 1 WHERE key = ?').run('feature_b');
console.log('\n2.2 Query again (still cached - WRONG data!)');
result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached} | Enabled flags: ${result.data.length}`);
console.log(` Flags:`, result.data.map(f => f.key).join(', '));
console.log(' NOTE: Still showing 2 flags, but should be 3!');
console.log('\n=== Test 3: Manual Cache Invalidation ===');
console.log('3.1 Invalidating flag cache');
const invalidated = await engine.invalidateCache('flags');
console.log(` Invalidated ${invalidated} entries`);
console.log('\n3.2 Query after invalidation (NOT cached - correct data)');
result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached} | Enabled flags: ${result.data.length}`);
console.log(` Flags:`, result.data.map(f => f.key).join(', '));
console.log(' NOW showing correct 3 flags!');
console.log('\n=== Test 4: Cross-Entity Cache Test ===');
console.log('4.1 Query experiments (NOT cached)');
let expResult = await engine.query(experimentQuery);
console.log(` Cached: ${expResult.metadata.cached}`);
console.log(` Results:`, expResult.data);
console.log('\n4.2 Query experiments again (cached)');
expResult = await engine.query(experimentQuery);
console.log(` Cached: ${expResult.metadata.cached}`);
console.log('\n4.3 Invalidate only flags (not experiments)');
await engine.invalidateCache('flags');
console.log('\n4.4 Query experiments (should still be cached)');
expResult = await engine.query(experimentQuery);
console.log(` Cached: ${expResult.metadata.cached} Experiments cache unaffected`);
console.log('\n=== Test 5: Full Cache Clear ===');
console.log('5.1 Clear all caches');
engine.clearCaches();
console.log('\n5.2 Query flags (NOT cached)');
result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached}`);
console.log('\n5.3 Query experiments (NOT cached)');
expResult = await engine.query(experimentQuery);
console.log(` Cached: ${expResult.metadata.cached}`);
console.log('\n=== Test 6: Simulating Sync Process ===');
console.log('6.1 Cache some queries');
await engine.query(flagQuery);
await engine.query(experimentQuery);
console.log('\n6.2 Simulate sync detecting changes to flags');
console.log(' - Adding new flag');
db.prepare('INSERT INTO flags (id, key, name, enabled, project_id) VALUES (?, ?, ?, ?, ?)').run('flag4', 'feature_d', 'Feature D', 1, 'proj1');
console.log(' - Invalidating flag cache');
await engine.invalidateCache('flags');
console.log('\n6.3 Query flags (NOT cached - includes new flag)');
result = await engine.query(flagQuery);
console.log(` Cached: ${result.metadata.cached} | Enabled flags: ${result.data.length}`);
console.log(` Flags:`, result.data.map(f => f.key).join(', '));
console.log('\n=== Test 7: Cache Statistics ===');
const stats = engine.getCacheMetrics();
console.log(`Cache metrics:`);
console.log(` Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
console.log(` Entries: ${stats.entries}`);
console.log(` Size: ${(stats.sizeBytes / 1024).toFixed(1)} KB`);
// Shutdown
await engine.shutdown();
console.log('\nCache invalidation tests completed!');
console.log('\nš Summary:');
console.log(' - Cache correctly stores query results');
console.log(' - Data updates are NOT automatically detected (by design)');
console.log(' - Manual invalidation correctly clears affected caches');
console.log(' - Entity-specific invalidation works (flags vs experiments)');
console.log(' - Full cache clear works for all entities');
console.log('\n IMPORTANT: Sync process MUST call invalidateCache() when data changes!');
}
catch (error) {
console.error('Test failed:', error);
process.exit(1);
}
}
testCacheInvalidation();
//# sourceMappingURL=test-cache-invalidation.js.map