@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
277 lines • 11 kB
JavaScript
/**
* Phase 4C Test Suite - Enhanced Field Disambiguation
*
* Tests the ability to handle ambiguous field names across tables.
* When multiple tables have fields with the same name (e.g., 'name', 'key', 'id'),
* the system should intelligently disambiguate or allow explicit table prefixes.
*
* Key scenarios:
* 1. Automatic disambiguation based on primary entity
* 2. Explicit table prefixes (flags.name vs events.name)
* 3. JOIN queries with ambiguous fields
* 4. Error handling for truly ambiguous cases
*/
import { IntelligentQueryEngine } from './IntelligentQueryEngine.js';
import { OptimizelyAdapter } from './adapters/OptimizelyAdapter.js';
import { getLogger } from '../../logging/Logger.js';
import Database from 'better-sqlite3';
const logger = getLogger();
export class FieldDisambiguationTester {
engine;
results = [];
db;
constructor(dbPath = './data/optimizely-cache.db') {
// Open database
this.db = new Database(dbPath, { readonly: true });
// Initialize engine with configuration
this.engine = new IntelligentQueryEngine({
discovery: {
autoDiscover: true,
discoveryInterval: 3600000,
cacheTTL: 60000
},
execution: {
defaultStrategy: 'hybrid-sql-first',
maxQueryTime: 30000,
enableParallel: false,
parallelThreshold: 1000
}
});
// Register Optimizely adapter
const optimizelyAdapter = new OptimizelyAdapter({ database: this.db });
this.engine.registerAdapter(optimizelyAdapter);
}
/**
* Run single test
*/
async runSingleTest(name, query, expectedBehavior) {
const startTime = Date.now();
try {
console.info(`Running test: ${name}`);
const result = await this.engine.query(query);
const success = result.data && (result.data.length > 0 || !!query.aggregations);
return {
testName: name,
query,
expectedBehavior,
actualResult: result,
success: success || false,
executionTime: Date.now() - startTime,
error: undefined
};
}
catch (error) {
return {
testName: name,
query,
expectedBehavior,
actualResult: null,
success: false,
executionTime: Date.now() - startTime,
error: error instanceof Error ? error.message : String(error)
};
}
}
/**
* Test automatic disambiguation
*/
async testAutomaticDisambiguation() {
const tests = [];
// Test 1: Simple field reference should use primary entity
tests.push(await this.runSingleTest('Automatic disambiguation - name field on flags', {
find: 'flags',
select: ['name', 'key'],
where: [{ field: 'name', operator: 'LIKE', value: '%test%' }]
}, 'Should automatically resolve "name" to "flags.name"'));
// Test 2: Multiple ambiguous fields in single query
tests.push(await this.runSingleTest('Multiple ambiguous fields', {
find: 'events',
select: ['name', 'key', 'description'],
where: [
{ field: 'name', operator: 'LIKE', value: '%click%' },
{ field: 'key', operator: 'LIKE', value: '%event%' }
]
}, 'Should resolve all fields to events table'));
return tests;
}
/**
* Test explicit table prefixes
*/
async testExplicitTablePrefixes() {
const tests = [];
// Test 1: Explicit table prefix
tests.push(await this.runSingleTest('Explicit prefix - flags.name', {
find: 'flags',
select: ['flag.name', 'flag.key', 'flags.description'],
where: [{ field: 'flag.archived', operator: '=', value: false }]
}, 'Should handle explicit table prefixes correctly'));
// Test 2: Mixed explicit and implicit
tests.push(await this.runSingleTest('Mixed prefixes', {
find: 'events',
select: ['event.name', 'key', 'event_type'],
where: [{ field: 'event.event_type', operator: '=', value: 'custom' }]
}, 'Should handle mix of explicit and implicit field references'));
return tests;
}
/**
* Test JOIN disambiguation
*/
async testJoinDisambiguation() {
const tests = [];
// Test 1: JOIN with same field names
tests.push(await this.runSingleTest('JOIN disambiguation - flags with environments', {
find: 'flags',
select: ['flag.name', 'flag_environment.environment_key'],
where: [
{ field: 'flag.archived', operator: '=', value: false },
{ field: 'flag_environment.enabled', operator: '=', value: true }
]
}, 'Should disambiguate fields in JOIN queries'));
// Test 2: Complex multi-table JOIN
tests.push(await this.runSingleTest('Multi-table JOIN disambiguation', {
find: 'variations',
select: [
'variation.name',
'variation.key',
'flags.name as flag_name',
'flags.key as flag_key'
],
where: [
{ field: 'variation.archived', operator: '=', value: false }
]
}, 'Should handle aliasing and disambiguation in complex JOINs'));
return tests;
}
/**
* Test ambiguity detection and errors
*/
async testAmbiguityErrors() {
const tests = [];
// Test 1: Truly ambiguous reference in JOIN
tests.push(await this.runSingleTest('Ambiguous field in JOIN context', {
find: 'flags',
select: ['name', 'environment_key'], // ambiguous without prefix
where: []
}, 'Should either auto-resolve or provide helpful error'));
// Test 2: Non-existent table prefix
tests.push(await this.runSingleTest('Invalid table prefix', {
find: 'flags',
select: ['nonexistent.name'],
where: []
}, 'Should provide clear error about invalid table prefix'));
return tests;
}
/**
* Test field aliasing
*/
async testFieldAliasing() {
const tests = [];
// Test 1: Simple aliasing
tests.push(await this.runSingleTest('Simple field aliasing', {
find: 'flags',
select: ['name as flag_name', 'key as flag_key'],
where: []
}, 'Should support field aliasing with AS clause'));
// Test 2: Aliasing in JOINs
tests.push(await this.runSingleTest('JOIN with aliasing', {
find: 'variations',
select: [
'variations.name as variation_name',
'flags.name as flag_name',
'variation.key',
'flags.key as flag_key'
],
where: []
}, 'Should support aliasing in JOIN queries'));
return tests;
}
/**
* Test aggregation disambiguation
*/
async testAggregationDisambiguation() {
const tests = [];
// Test 1: Aggregation with ambiguous groupBy
tests.push(await this.runSingleTest('Aggregation groupBy disambiguation', {
find: 'flags',
select: ['archived'],
groupBy: ['archived'],
aggregations: [{
field: '*',
function: 'COUNT',
alias: 'count'
}]
}, 'Should disambiguate groupBy fields'));
// Test 2: Aggregation with JOIN and ambiguous fields
tests.push(await this.runSingleTest('JOIN aggregation disambiguation', {
find: 'variations',
select: ['flag.name', 'COUNT(variations.key) as variation_count'],
groupBy: ['flag.name'],
where: []
}, 'Should handle disambiguation in aggregated JOINs'));
return tests;
}
/**
* Run all tests
*/
async runAllTests() {
console.log('# Phase 4C Field Disambiguation Test Suite\n');
const testGroups = [
{ name: 'Automatic Disambiguation', fn: () => this.testAutomaticDisambiguation() },
{ name: 'Explicit Table Prefixes', fn: () => this.testExplicitTablePrefixes() },
{ name: 'JOIN Disambiguation', fn: () => this.testJoinDisambiguation() },
{ name: 'Ambiguity Errors', fn: () => this.testAmbiguityErrors() },
{ name: 'Field Aliasing', fn: () => this.testFieldAliasing() },
{ name: 'Aggregation Disambiguation', fn: () => this.testAggregationDisambiguation() }
];
for (const group of testGroups) {
console.log(`\\n## ${group.name}`);
const results = await group.fn();
this.results.push(...results);
for (const result of results) {
const status = result.success ? '✅' : '❌';
console.log(`${status} ${result.testName}`);
if (!result.success && result.error) {
console.log(` Error: ${result.error}`);
}
console.log(` Expected: ${result.expectedBehavior}`);
console.log(` Time: ${result.executionTime}ms`);
}
}
this.printSummary();
}
/**
* Print test summary
*/
printSummary() {
const total = this.results.length;
const passed = this.results.filter(r => r.success).length;
const failed = total - passed;
const successRate = ((passed / total) * 100).toFixed(1);
console.log('\\n## Summary');
console.log(`- Total Tests: ${total}`);
console.log(`- Passed: ${passed}`);
console.log(`- Failed: ${failed}`);
console.log(`- Success Rate: ${successRate}%`);
if (failed > 0) {
console.log('\\n## Failed Tests:');
this.results.filter(r => !r.success).forEach(r => {
console.log(`- ${r.testName}: ${r.error || 'Unknown error'}`);
});
}
// Performance analysis
const times = this.results.map(r => r.executionTime);
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
const maxTime = Math.max(...times);
const minTime = Math.min(...times);
console.log('\\n## Performance');
console.log(`- Average: ${avgTime.toFixed(1)}ms`);
console.log(`- Min: ${minTime}ms`);
console.log(`- Max: ${maxTime}ms`);
}
}
// Run tests if executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
const tester = new FieldDisambiguationTester();
tester.runAllTests().catch(console.error);
}
//# sourceMappingURL=test-phase4c-field-disambiguation.js.map