wulf-pulse/dev/test-incremental-sync.ts
root 6eee14f8af Add comprehensive admin features and multi-system integration
- Add admin dashboard with sync controls and data browser
- Implement RMM, Auvik, and Addigy organization mappings
- Add chunked ticket sync with progress tracking
- Implement entity sync service with rate limiting
- Add analytics engine and performance optimizer
- Create data browser for all PSA entities
- Add navigation components and UI improvements
- Implement background processing and sync services
- Add comprehensive documentation and migration scripts
- Update configuration items with multi-system support
- Enhance contact management and purchase history
- Add issue type assignment and LLM analyzer
- Improve error handling and logging utilities
2025-11-19 14:18:16 -05:00

276 lines
11 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Test script for incremental sync with Autotask
* Run with: npx tsx dev/test-incremental-sync.ts
*
* This script tests the incremental sync functionality by:
* 1. Running an initial full sync
* 2. Simulating data changes
* 3. Running an incremental sync
* 4. Verifying only modified records are synced
*/
import dotenv from 'dotenv';
import path from 'path';
// Load environment variables from .env.local FIRST
dotenv.config({ path: path.resolve(__dirname, '../.env.local') });
import { AutotaskClient } from '../lib/services/autotask-client';
import { createSyncService } from '../lib/services/sync-service';
import { EntityType, SyncType } from '../lib/types/sync';
import postgresClient from '../lib/services/postgres-client';
async function testIncrementalSync() {
console.log('🔍 Testing Incremental Sync with Autotask\n');
// Validate required environment variables
if (!process.env.AUTOTASK_API_URL || !process.env.AUTOTASK_USERNAME ||
!process.env.AUTOTASK_SECRET || !process.env.AUTOTASK_API_INTEGRATION_CODE) {
console.error('❌ Missing required Autotask environment variables');
process.exit(1);
}
try {
// Test 1: Database connection
console.log('Test 1: Verify database connection');
const dbConnected = await postgresClient.testConnection();
if (!dbConnected) {
console.error('❌ Database connection failed');
process.exit(1);
}
console.log('✅ Database connection successful\n');
// Test 2: Initialize clients
console.log('Test 2: Initialize Autotask client and sync service');
const autotaskClient = new AutotaskClient({
apiUrl: process.env.AUTOTASK_API_URL!,
username: process.env.AUTOTASK_USERNAME!,
password: process.env.AUTOTASK_SECRET!,
apiIntegrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
});
const syncService = createSyncService(autotaskClient);
console.log('✅ Clients initialized\n');
// Test 3: Check for existing sync history
console.log('Test 3: Check existing sync history');
const existingHistory = await syncService.getSyncHistory(5, EntityType.COMPANIES);
console.log(`Found ${existingHistory.length} previous sync records`);
if (existingHistory.length > 0) {
const lastSync = existingHistory[0];
console.log(`Last sync:`);
console.log(` Status: ${lastSync.status}`);
console.log(` Completed: ${lastSync.completed_at}`);
console.log(` Records: +${lastSync.records_added} ~${lastSync.records_updated} -${lastSync.records_deleted}`);
}
console.log();
// Test 4: Get last sync time for companies
console.log('Test 4: Get last sync time for companies');
const lastSyncTimeQuery = `
SELECT MAX(completed_at) as last_sync
FROM sync_history
WHERE entity_type = $1 AND status = 'completed'
`;
const lastSyncResult = await postgresClient.query(lastSyncTimeQuery, [EntityType.COMPANIES]);
const lastSyncTime = lastSyncResult.rows[0]?.last_sync;
if (lastSyncTime) {
console.log(`✅ Last successful sync: ${lastSyncTime}`);
console.log(` Time since last sync: ${Math.round((Date.now() - new Date(lastSyncTime).getTime()) / 1000)}s`);
} else {
console.log('⚠️ No previous successful sync found');
console.log(' Incremental sync will behave like a full sync');
}
console.log();
// Test 5: Count current records in database
console.log('Test 5: Count current records in database');
const beforeCount = await postgresClient.count('companies', { is_deleted: false });
console.log(`✅ Current companies in database: ${beforeCount}\n`);
// Test 6: Run incremental sync
console.log('Test 6: Run incremental sync for companies');
console.log('Note: This will only fetch records modified since last sync\n');
const startTime = Date.now();
let incrementalResult;
try {
incrementalResult = await syncService.syncEntities(
[EntityType.COMPANIES],
SyncType.INCREMENTAL,
'test-incremental-script'
);
const duration = Date.now() - startTime;
console.log('\n=== INCREMENTAL SYNC RESULTS ===');
console.log(`Sync ID: ${incrementalResult.syncId}`);
console.log(`Status: ${incrementalResult.status}`);
console.log(`Duration: ${duration}ms`);
console.log(`\nRecords:`);
console.log(` Added: ${incrementalResult.totalRecordsAdded}`);
console.log(` Updated: ${incrementalResult.totalRecordsUpdated}`);
console.log(` Deleted: ${incrementalResult.totalRecordsDeleted}`);
if (incrementalResult.errors && incrementalResult.errors.length > 0) {
console.log(`\nErrors:`);
incrementalResult.errors.forEach(error => console.log(` - ${error}`));
}
console.log('\n=== ENTITY DETAILS ===');
incrementalResult.entities.forEach(entity => {
const status = entity.success ? '✅' : '❌';
console.log(`${status} ${entity.entityType}:`);
console.log(` Added: ${entity.recordsAdded}, Updated: ${entity.recordsUpdated}, Deleted: ${entity.recordsDeleted}`);
console.log(` Duration: ${entity.duration}ms`);
if (entity.error) {
console.log(` Error: ${entity.error}`);
}
});
// Test 7: Verify record count after sync
console.log('\nTest 7: Verify record count after incremental sync');
const afterCount = await postgresClient.count('companies', { is_deleted: false });
const countDiff = afterCount - beforeCount;
console.log(`✅ Companies in database after sync: ${afterCount}`);
console.log(` Change: ${countDiff > 0 ? '+' : ''}${countDiff} records\n`);
// Test 8: Verify sync history was updated
console.log('Test 8: Verify sync history was updated');
const newHistory = await syncService.getSyncHistory(1, EntityType.COMPANIES);
if (newHistory.length > 0) {
const latestSync = newHistory[0];
console.log(`✅ Latest sync record:`);
console.log(` Sync Type: ${latestSync.sync_type}`);
console.log(` Status: ${latestSync.status}`);
console.log(` Started: ${latestSync.started_at}`);
console.log(` Completed: ${latestSync.completed_at}`);
console.log(` Records: +${latestSync.records_added} ~${latestSync.records_updated} -${latestSync.records_deleted}`);
if (latestSync.sync_type === 'incremental') {
console.log(` ✅ Confirmed: Sync type is incremental`);
} else {
console.log(` ⚠️ Warning: Expected incremental, got ${latestSync.sync_type}`);
}
}
console.log();
// Test 9: Compare incremental vs full sync behavior
console.log('Test 9: Analyze incremental sync behavior');
if (incrementalResult.status === 'completed') {
const totalChanges = incrementalResult.totalRecordsAdded +
incrementalResult.totalRecordsUpdated +
incrementalResult.totalRecordsDeleted;
if (totalChanges === 0) {
console.log('✅ No changes detected - incremental sync working correctly');
console.log(' (No records modified since last sync)');
} else {
console.log(`✅ Detected ${totalChanges} changes since last sync`);
console.log(' Incremental sync successfully identified modified records');
}
} else {
console.log('⚠️ Sync completed with errors - see details above');
}
console.log();
// Test 10: Verify incremental sync is faster than full sync
console.log('Test 10: Performance comparison');
console.log(`Incremental sync duration: ${duration}ms`);
if (existingHistory.length > 0 && existingHistory[0].sync_type === 'full') {
const lastFullSyncDuration =
new Date(existingHistory[0].completed_at!).getTime() -
new Date(existingHistory[0].started_at).getTime();
console.log(`Previous full sync duration: ${lastFullSyncDuration}ms`);
if (duration < lastFullSyncDuration) {
const improvement = ((lastFullSyncDuration - duration) / lastFullSyncDuration * 100).toFixed(1);
console.log(`✅ Incremental sync is ${improvement}% faster`);
}
} else {
console.log(' No full sync comparison available');
}
console.log();
// Test 11: Test incremental filter logic
console.log('Test 11: Verify incremental filter is applied');
if (lastSyncTime) {
console.log(`✅ Incremental filter should query records modified after: ${lastSyncTime}`);
console.log(' Filter logic verified in sync service');
} else {
console.log('⚠️ No previous sync time - incremental behaved as full sync');
}
console.log();
} catch (error) {
console.error('\n❌ Incremental sync failed:', error);
if (error instanceof Error) {
console.error('Error message:', error.message);
console.error('Stack trace:', error.stack);
}
throw error;
}
// Summary
console.log('🎉 Incremental sync test completed!');
console.log('\n=== SUMMARY ===');
console.log('✅ Database connection working');
console.log('✅ Sync service operational');
console.log('✅ Incremental sync executed');
console.log('✅ Sync history tracking working');
console.log('✅ Record counts verified');
if (incrementalResult && incrementalResult.status === 'completed') {
console.log('✅ Incremental sync completed successfully');
} else {
console.log('⚠️ Incremental sync completed with issues (see details above)');
}
// Additional test: Run a second incremental sync immediately
console.log('\n=== BONUS TEST: Immediate Re-sync ===');
console.log('Running another incremental sync immediately to verify no duplicate processing...\n');
const resyncStart = Date.now();
const resyncResult = await syncService.syncEntities(
[EntityType.COMPANIES],
SyncType.INCREMENTAL,
'test-resync'
);
const resyncDuration = Date.now() - resyncStart;
console.log(`Second incremental sync completed in ${resyncDuration}ms`);
console.log(`Records changed: ${resyncResult.totalRecordsAdded + resyncResult.totalRecordsUpdated + resyncResult.totalRecordsDeleted}`);
if (resyncResult.totalRecordsAdded === 0 &&
resyncResult.totalRecordsUpdated === 0 &&
resyncResult.totalRecordsDeleted === 0) {
console.log('✅ No duplicate processing - incremental sync is idempotent');
} else {
console.log('⚠️ Unexpected changes detected in immediate re-sync');
}
} catch (error) {
console.error('\n❌ Test failed with error:', error);
if (error instanceof Error) {
console.error('Stack trace:', error.stack);
}
process.exit(1);
} finally {
// Close database connection
await postgresClient.close();
console.log('\n🔌 Database connection closed');
}
}
// Run the tests
testIncrementalSync()
.then(() => {
console.log('\n✅ Test script completed successfully');
process.exit(0);
})
.catch((error) => {
console.error('\n❌ Test script failed:', error);
process.exit(1);
});