Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions experiments/test-refactoring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env node

/**
* Simple test script to validate ccxtInstanceManager refactoring
* This script verifies that:
* 1. ccxtInstanceManager is properly exported
* 2. No direct CCXT instantiations remain except in fallback cases
* 3. ccxtBrowserProvider delegates to ccxtInstanceManager
*/

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log('πŸ§ͺ Testing CCXT Instance Manager Refactoring');
console.log('============================================');

// Test 1: Verify ccxtInstanceManager export
console.log('\n1. Checking ccxtInstanceManager export...');
const ccxtInstanceManagerPath = '../src/store/utils/ccxtInstanceManager.ts';
const ccxtInstanceManagerContent = fs.readFileSync(path.join(__dirname, ccxtInstanceManagerPath), 'utf8');

if (ccxtInstanceManagerContent.includes('export const ccxtInstanceManager = new CCXTInstanceManager()')) {
console.log('βœ… ccxtInstanceManager properly exported as singleton');
} else {
console.log('❌ ccxtInstanceManager export not found');
}

// Test 2: Verify TestChartWidget.tsx uses ccxtInstanceManager
console.log('\n2. Checking TestChartWidget.tsx...');
const testChartWidgetPath = '../src/components/TestChartWidget.tsx';
const testChartWidgetContent = fs.readFileSync(path.join(__dirname, testChartWidgetPath), 'utf8');

if (testChartWidgetContent.includes('import { ccxtInstanceManager }')) {
console.log('βœ… TestChartWidget imports ccxtInstanceManager');
} else {
console.log('❌ TestChartWidget does not import ccxtInstanceManager');
}

if (testChartWidgetContent.includes('ccxtInstanceManager.getExchangeInstanceForMarket')) {
console.log('βœ… TestChartWidget uses ccxtInstanceManager.getExchangeInstanceForMarket');
} else {
console.log('❌ TestChartWidget does not use ccxtInstanceManager');
}

// Test 3: Verify TestTimeframes.tsx uses ccxtInstanceManager
console.log('\n3. Checking TestTimeframes.tsx...');
const testTimeframesPath = '../src/components/TestTimeframes.tsx';
const testTimeframesContent = fs.readFileSync(path.join(__dirname, testTimeframesPath), 'utf8');

if (testTimeframesContent.includes('import { ccxtInstanceManager }')) {
console.log('βœ… TestTimeframes imports ccxtInstanceManager');
} else {
console.log('❌ TestTimeframes does not import ccxtInstanceManager');
}

if (testTimeframesContent.includes('ccxtInstanceManager.getExchangeInstanceForMarket')) {
console.log('βœ… TestTimeframes uses ccxtInstanceManager.getExchangeInstanceForMarket');
} else {
console.log('❌ TestTimeframes does not use ccxtInstanceManager');
}

// Test 4: Verify ccxtBrowserProvider delegates to ccxtInstanceManager
console.log('\n4. Checking ccxtBrowserProvider.ts...');
const ccxtBrowserProviderPath = '../src/store/providers/ccxtBrowserProvider.ts';
const ccxtBrowserProviderContent = fs.readFileSync(path.join(__dirname, ccxtBrowserProviderPath), 'utf8');

if (ccxtBrowserProviderContent.includes('import { ccxtInstanceManager }')) {
console.log('βœ… ccxtBrowserProvider imports ccxtInstanceManager');
} else {
console.log('❌ ccxtBrowserProvider does not import ccxtInstanceManager');
}

if (ccxtBrowserProviderContent.includes('ccxtInstanceManager.getExchangeInstanceForMarket')) {
console.log('βœ… ccxtBrowserProvider delegates to ccxtInstanceManager.getExchangeInstanceForMarket');
} else {
console.log('❌ ccxtBrowserProvider does not delegate to ccxtInstanceManager');
}

if (ccxtBrowserProviderContent.includes('ccxtInstanceManager.clearCache')) {
console.log('βœ… ccxtBrowserProvider delegates cache clearing to ccxtInstanceManager');
} else {
console.log('❌ ccxtBrowserProvider does not delegate cache clearing');
}

// Test 5: Check for duplicate caching code removal
console.log('\n5. Checking for duplicate cache removal...');

if (!ccxtBrowserProviderContent.includes('private static instancesCache = new Map')) {
console.log('βœ… Duplicate static instancesCache removed from ccxtBrowserProvider');
} else {
console.log('❌ Duplicate static instancesCache still exists in ccxtBrowserProvider');
}

if (!ccxtBrowserProviderContent.includes('setInterval(() => {') ||
ccxtBrowserProviderContent.includes('Cleanup Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ управляСтся Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎ')) {
console.log('βœ… Duplicate cleanup interval removed or replaced');
} else {
console.log('❌ Duplicate cleanup interval still exists');
}

// Test 6: Check for remaining direct instantiations
console.log('\n6. Checking for direct CCXT instantiations...');

const filesToCheck = [
testChartWidgetPath,
testTimeframesPath,
ccxtBrowserProviderPath
];

let foundDirectInstantiations = 0;
let expectedFallbackInstantiations = 0;

filesToCheck.forEach(filePath => {
const content = fs.readFileSync(path.join(__dirname, filePath), 'utf8');
const directInstantiations = (content.match(/new ccxt\./g) || []).length;
const fallbackInstantiations = (content.match(/Fallback|fallback|CCXT Pro.*fallback/gi) || []).length;

foundDirectInstantiations += directInstantiations;
expectedFallbackInstantiations += fallbackInstantiations;

if (directInstantiations > 0) {
console.log(` - ${path.basename(filePath)}: ${directInstantiations} direct instantiation(s) found`);
}
});

if (foundDirectInstantiations <= expectedFallbackInstantiations) {
console.log('βœ… Direct CCXT instantiations limited to fallback cases only');
} else {
console.log(`❌ Found ${foundDirectInstantiations} direct instantiations, expected only ${expectedFallbackInstantiations} fallback cases`);
}

console.log('\n🎯 Refactoring Summary:');
console.log('- ccxtInstanceManager is now used as the central caching mechanism');
console.log('- Test components use ccxtInstanceManager instead of direct instantiation');
console.log('- ccxtBrowserProvider delegates to ccxtInstanceManager');
console.log('- Duplicate caching code has been removed');
console.log('- CCXT Pro has fallback logic until ccxtInstanceManager supports it');

console.log('\nβœ… Refactoring validation completed!');
33 changes: 25 additions & 8 deletions src/components/TestChartWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useDataProviderStore } from '../store/dataProviderStore';
import { ccxtInstanceManager } from '../store/utils/ccxtInstanceManager';

export const TestChartWidget: React.FC = () => {
const {
Expand Down Expand Up @@ -33,17 +34,33 @@ export const TestChartWidget: React.FC = () => {
addResult(`Provider for Binance: ${binanceProvider ? binanceProvider.name : 'None'}`);
};

const testCCXT = () => {
const testCCXT = async () => {
addResult('=== Testing CCXT ===');
try {
const ccxt = (window as any).ccxt;
if (ccxt) {
addResult(`CCXT loaded: version ${ccxt.version || 'unknown'}`);
addResult(`Available exchanges: ${Object.keys(ccxt).filter(k => typeof ccxt[k] === 'function').slice(0, 5).join(', ')}...`);

// Test Binance instance creation
const binance = new ccxt.binance();
addResult(`Binance instance created: ${binance.id}`);

// Test Binance instance creation using ccxtInstanceManager
try {
const binance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
addResult(`βœ… Binance instance created via ccxtInstanceManager: ${binance.id}`);
} catch (managerError) {
addResult(`❌ ccxtInstanceManager error: ${managerError}`);
// Fallback to direct instantiation for comparison
const binance = new ccxt.binance();
addResult(`⚠️ Fallback - Binance instance created directly: ${binance.id}`);
}
Comment on lines +45 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fallback creates inconsistent instance without credentials.

The fallback at line 61 instantiates ccxt.binance() without credentials, while the manager call (lines 47-56) passes apiKey, secret, and sandbox configuration. This inconsistency means the fallback won't actually test the same scenario and could mask configuration or authentication issues in ccxtInstanceManager.

Apply this diff to align the fallback with the manager configuration:

         } catch (managerError) {
           addResult(`❌ ccxtInstanceManager error: ${managerError}`);
           // Fallback to direct instantiation for comparison
-          const binance = new ccxt.binance();
+          const binance = new ccxt.binance({
+            apiKey: 'test',
+            secret: 'test',
+            sandbox: true
+          });
           addResult(`⚠️ Fallback - Binance instance created directly: ${binance.id}`);
         }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Test Binance instance creation using ccxtInstanceManager
try {
const binance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
addResult(`βœ… Binance instance created via ccxtInstanceManager: ${binance.id}`);
} catch (managerError) {
addResult(`❌ ccxtInstanceManager error: ${managerError}`);
// Fallback to direct instantiation for comparison
const binance = new ccxt.binance();
addResult(`⚠️ Fallback - Binance instance created directly: ${binance.id}`);
}
// Test Binance instance creation using ccxtInstanceManager
try {
const binance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
addResult(`βœ… Binance instance created via ccxtInstanceManager: ${binance.id}`);
} catch (managerError) {
addResult(`❌ ccxtInstanceManager error: ${managerError}`);
// Fallback to direct instantiation for comparison
const binance = new ccxt.binance({
apiKey: 'test',
secret: 'test',
sandbox: true
});
addResult(`⚠️ Fallback - Binance instance created directly: ${binance.id}`);
}
πŸ€– Prompt for AI Agents
In src/components/TestChartWidget.tsx around lines 45 to 63, the fallback
instantiates ccxt.binance() without credentials which is inconsistent with the
earlier manager call that passes apiKey, secret and sandbox; change the fallback
to create the Binance instance with the same credentials and sandbox
configuration used above (pass the same apiKey and secret when constructing the
ccxt.binance instance and enable sandbox/testnet mode if the ccxt exchange
exposes a setSandboxMode or equivalent option) so the fallback tests the same
authentication/configuration scenario as ccxtInstanceManager.

} else {
addResult('❌ CCXT not loaded');
}
Expand Down Expand Up @@ -98,7 +115,7 @@ export const TestChartWidget: React.FC = () => {
const runAllTests = async () => {
setTestResults([]);
testProviders();
testCCXT();
await testCCXT();
await testInitializeChartData();
await testSubscription();
addResult('=== All Tests Completed ===');
Expand Down Expand Up @@ -129,8 +146,8 @@ export const TestChartWidget: React.FC = () => {
Test Providers
</button>

<button
onClick={testCCXT}
<button
onClick={() => testCCXT()}
className="px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
>
Test CCXT
Expand Down
90 changes: 57 additions & 33 deletions src/components/TestTimeframes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import { useDataProviderStore } from '../store/dataProviderStore';
import { getCCXT } from '../store/utils/ccxtUtils';
import { ccxtInstanceManager } from '../store/utils/ccxtInstanceManager';

const TestTimeframes: React.FC = () => {
const { getTimeframesForExchange } = useDataProviderStore();
Expand All @@ -10,43 +11,66 @@ const TestTimeframes: React.FC = () => {
const testExchanges = ['binance', 'bybit', 'okx', 'kucoin'];
const testResults: Record<string, any> = {};

// Test direct CCXT access
console.log('πŸ§ͺ [TestTimeframes] Testing direct CCXT access');
const ccxt = getCCXT();
if (ccxt) {
console.log('βœ… [TestTimeframes] CCXT is available');
console.log('πŸ“‹ [TestTimeframes] Available exchanges:', Object.keys(ccxt).filter(key => typeof ccxt[key] === 'function').slice(0, 10));

// Test specific exchange
if (ccxt.binance) {
const runTests = async () => {
// Test direct CCXT access
console.log('πŸ§ͺ [TestTimeframes] Testing direct CCXT access');
const ccxt = getCCXT();
if (ccxt) {
console.log('βœ… [TestTimeframes] CCXT is available');
console.log('πŸ“‹ [TestTimeframes] Available exchanges:', Object.keys(ccxt).filter(key => typeof ccxt[key] === 'function').slice(0, 10));

// Test specific exchange using ccxtInstanceManager
if (ccxt.binance) {
try {
const binanceInstance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-timeframes-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
console.log('πŸ“Š [TestTimeframes] Binance timeframes via ccxtInstanceManager:', binanceInstance.timeframes);
testResults.binance_direct = binanceInstance.timeframes;
} catch (error) {
console.error('❌ [TestTimeframes] Error creating Binance instance via ccxtInstanceManager:', error);
testResults.binance_direct = { error: error.message };

// Fallback to direct instantiation for comparison
try {
const binanceInstanceFallback = new ccxt.binance();
console.log('⚠️ [TestTimeframes] Fallback - Binance timeframes:', binanceInstanceFallback.timeframes);
testResults.binance_direct = binanceInstanceFallback.timeframes;
} catch (fallbackError) {
console.error('❌ [TestTimeframes] Fallback also failed:', fallbackError);
testResults.binance_direct = { error: fallbackError.message };
}
}
Comment on lines +25 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fallback inconsistency and result overwriting issue.

Two problems here:

  1. The fallback at line 43 creates a Binance instance without the credentials that were passed to ccxtInstanceManager (lines 28-32), similar to the issue in TestChartWidget.tsx. This makes the fallback test a different scenario.

  2. Lines 36 and 45 both assign to testResults.binance_direct, so if the manager succeeds, that result is immediately overwritten by the fallback attempt. The fallback block should only assign on fallback success.

Apply this diff:

           const binanceInstance = await ccxtInstanceManager.getExchangeInstanceForMarket(
             'binance',
             'test-timeframes-account',
             {
               apiKey: 'test',
               secret: 'test',
               sandbox: true
             },
             'spot'
           );
           console.log('πŸ“Š [TestTimeframes] Binance timeframes via ccxtInstanceManager:', binanceInstance.timeframes);
           testResults.binance_direct = binanceInstance.timeframes;
         } catch (error) {
           console.error('❌ [TestTimeframes] Error creating Binance instance via ccxtInstanceManager:', error);
-          testResults.binance_direct = { error: error.message };
 
           // Fallback to direct instantiation for comparison
           try {
-            const binanceInstanceFallback = new ccxt.binance();
+            const binanceInstanceFallback = new ccxt.binance({
+              apiKey: 'test',
+              secret: 'test',
+              sandbox: true
+            });
             console.log('⚠️ [TestTimeframes] Fallback - Binance timeframes:', binanceInstanceFallback.timeframes);
             testResults.binance_direct = binanceInstanceFallback.timeframes;
           } catch (fallbackError) {
             console.error('❌ [TestTimeframes] Fallback also failed:', fallbackError);
             testResults.binance_direct = { error: fallbackError.message };
           }
         }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const binanceInstance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-timeframes-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
console.log('πŸ“Š [TestTimeframes] Binance timeframes via ccxtInstanceManager:', binanceInstance.timeframes);
testResults.binance_direct = binanceInstance.timeframes;
} catch (error) {
console.error('❌ [TestTimeframes] Error creating Binance instance via ccxtInstanceManager:', error);
testResults.binance_direct = { error: error.message };
// Fallback to direct instantiation for comparison
try {
const binanceInstanceFallback = new ccxt.binance();
console.log('⚠️ [TestTimeframes] Fallback - Binance timeframes:', binanceInstanceFallback.timeframes);
testResults.binance_direct = binanceInstanceFallback.timeframes;
} catch (fallbackError) {
console.error('❌ [TestTimeframes] Fallback also failed:', fallbackError);
testResults.binance_direct = { error: fallbackError.message };
}
}
const binanceInstance = await ccxtInstanceManager.getExchangeInstanceForMarket(
'binance',
'test-timeframes-account',
{
apiKey: 'test',
secret: 'test',
sandbox: true
},
'spot'
);
console.log('πŸ“Š [TestTimeframes] Binance timeframes via ccxtInstanceManager:', binanceInstance.timeframes);
testResults.binance_direct = binanceInstance.timeframes;
} catch (error) {
console.error('❌ [TestTimeframes] Error creating Binance instance via ccxtInstanceManager:', error);
// Fallback to direct instantiation for comparison
try {
const binanceInstanceFallback = new ccxt.binance({
apiKey: 'test',
secret: 'test',
sandbox: true
});
console.log('⚠️ [TestTimeframes] Fallback - Binance timeframes:', binanceInstanceFallback.timeframes);
testResults.binance_direct = binanceInstanceFallback.timeframes;
} catch (fallbackError) {
console.error('❌ [TestTimeframes] Fallback also failed:', fallbackError);
testResults.binance_direct = { error: fallbackError.message };
}
}
πŸ€– Prompt for AI Agents
In src/components/TestTimeframes.tsx around lines 25 to 50, the fallback logic
is creating a Binance instance without the same credentials/sandbox passed to
ccxtInstanceManager and is overwriting testResults.binance_direct even when the
manager succeeded; fix by (1) in the catch fallback, instantiate the
ccxt.binance with the same options used in the manager call (apiKey, secret,
sandbox) so it tests the same scenario, and (2) only assign
testResults.binance_direct inside the fallback success branch (do not overwrite
the value set by the manager success path); keep the original manager assignment
unchanged and ensure fallback assignment happens only if the manager call failed
and the fallback succeeds.

}
} else {
console.error('❌ [TestTimeframes] CCXT is not available');
testResults.ccxt_available = false;
}

// Test our store method
testExchanges.forEach(exchange => {
console.log(`πŸ§ͺ [TestTimeframes] Testing ${exchange}`);
try {
const binanceInstance = new ccxt.binance();
console.log('πŸ“Š [TestTimeframes] Binance timeframes:', binanceInstance.timeframes);
testResults.binance_direct = binanceInstance.timeframes;
const timeframes = getTimeframesForExchange(exchange);
testResults[exchange] = timeframes;
console.log(`βœ… [TestTimeframes] ${exchange} timeframes:`, timeframes);
} catch (error) {
console.error('❌ [TestTimeframes] Error creating Binance instance:', error);
testResults.binance_direct = { error: error.message };
console.error(`❌ [TestTimeframes] Error getting timeframes for ${exchange}:`, error);
testResults[exchange] = { error: error.message };
}
}
} else {
console.error('❌ [TestTimeframes] CCXT is not available');
testResults.ccxt_available = false;
}

// Test our store method
testExchanges.forEach(exchange => {
console.log(`πŸ§ͺ [TestTimeframes] Testing ${exchange}`);
try {
const timeframes = getTimeframesForExchange(exchange);
testResults[exchange] = timeframes;
console.log(`βœ… [TestTimeframes] ${exchange} timeframes:`, timeframes);
} catch (error) {
console.error(`❌ [TestTimeframes] Error getting timeframes for ${exchange}:`, error);
testResults[exchange] = { error: error.message };
}
});
});

setResults(testResults);
};

setResults(testResults);
runTests();
}, [getTimeframesForExchange]);

return (
Expand Down
Loading