import { test } from '@git.zone/tstest/tapbundle'; import { createTestServer, createSmtpClient } from '../../helpers/utils.js'; import { Email } from '../../../ts/mail/core/classes.email.js'; test('CPERF-08: DNS Caching Efficiency Performance Tests', async () => { console.log('\n🌐 Testing SMTP Client DNS Caching Efficiency'); console.log('=' .repeat(60)); // Scenario 1: DNS Resolution Caching await test.test('Scenario 1: DNS Resolution Caching', async () => { console.log('\nšŸ” Testing DNS resolution caching performance...'); let dnsLookupCount = 0; const originalLookup = require('dns').lookup; // Mock DNS lookup to track calls require('dns').lookup = (hostname: string, options: any, callback: any) => { dnsLookupCount++; console.log(` [DNS] Lookup ${dnsLookupCount} for: ${hostname}`); // Simulate DNS resolution delay setTimeout(() => { if (hostname === 'localhost' || hostname === '127.0.0.1') { callback(null, '127.0.0.1', 4); } else { callback(null, '127.0.0.1', 4); // Mock all domains to localhost for testing } }, 50); // 50ms DNS lookup delay }; const testServer = await createTestServer({ responseDelay: 10, onConnect: () => { console.log(' [Server] Connection established'); } }); try { console.log(' Creating SMTP client with connection pooling...'); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 3, maxMessages: 100, // DNS caching settings dnsCache: true, dnsCacheTtl: 5000 // 5 seconds TTL }); console.log(' Sending multiple emails to same domain...'); const emails = []; for (let i = 0; i < 10; i++) { emails.push(new Email({ from: 'sender@example.com', to: [`recipient${i}@example.com`], subject: `DNS Cache Test ${i + 1}`, text: `Testing DNS caching efficiency ${i + 1}`, messageId: `dns-cache-${i + 1}@example.com` })); } const startTime = Date.now(); const promises = emails.map((email, index) => { return smtpClient.sendMail(email).then(result => { console.log(` āœ“ Email ${index + 1} sent successfully`); return { success: true, result }; }).catch(error => { console.log(` āœ— Email ${index + 1} failed: ${error.message}`); return { success: false, error }; }); }); const results = await Promise.all(promises); const endTime = Date.now(); const successful = results.filter(r => r.success).length; const totalTime = endTime - startTime; console.log(` Total DNS lookups performed: ${dnsLookupCount}`); console.log(` Emails sent: ${emails.length}, Successful: ${successful}`); console.log(` Total time: ${totalTime}ms`); console.log(` DNS cache efficiency: ${dnsLookupCount < emails.length ? 'Good' : 'Poor'}`); console.log(` Expected 1-3 DNS lookups for ${emails.length} emails to same domain`); smtpClient.close(); } finally { // Restore original DNS lookup require('dns').lookup = originalLookup; testServer.close(); } }); // Scenario 2: Multiple Domain DNS Caching await test.test('Scenario 2: Multiple Domain DNS Caching', async () => { console.log('\nšŸŒ Testing DNS caching across multiple domains...'); let dnsLookupCount = 0; const dnsCache = new Map(); const originalLookup = require('dns').lookup; // Enhanced DNS mock with caching simulation require('dns').lookup = (hostname: string, options: any, callback: any) => { const now = Date.now(); const cached = dnsCache.get(hostname); if (cached && (now - cached.timestamp) < 3000) { // 3 second cache console.log(` [DNS] Cache hit for: ${hostname}`); setTimeout(() => callback(null, cached.ip, 4), 5); // Fast cache response return; } dnsLookupCount++; console.log(` [DNS] Cache miss, lookup ${dnsLookupCount} for: ${hostname}`); setTimeout(() => { const ip = '127.0.0.1'; dnsCache.set(hostname, { ip, timestamp: now }); callback(null, ip, 4); }, 75); // Slower DNS lookup }; const testServer = await createTestServer({ responseDelay: 10 }); try { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2, dnsCache: true, dnsCacheTtl: 3000 }); console.log(' Creating emails to multiple domains...'); const domains = ['domain1.com', 'domain2.com', 'domain3.com']; const emails = []; // Create multiple emails per domain domains.forEach((domain, domainIndex) => { for (let i = 0; i < 4; i++) { emails.push(new Email({ from: `sender@${domain}`, to: [`recipient${i}@${domain}`], subject: `Multi-domain DNS Test ${domainIndex + 1}-${i + 1}`, text: `Testing DNS caching for ${domain}`, messageId: `multi-dns-${domainIndex}-${i}@${domain}` })); } }); console.log(' Sending emails to test DNS caching across domains...'); const startTime = Date.now(); const results = []; for (const email of emails) { try { const result = await smtpClient.sendMail(email); results.push({ success: true, result }); console.log(` āœ“ Email to ${email.to[0]} sent`); } catch (error) { results.push({ success: false, error }); console.log(` āœ— Email to ${email.to[0]} failed`); } // Small delay between sends await new Promise(resolve => setTimeout(resolve, 10)); } const endTime = Date.now(); const successful = results.filter(r => r.success).length; console.log(` Total DNS lookups: ${dnsLookupCount}`); console.log(` Unique domains: ${domains.length}`); console.log(` Total emails: ${emails.length}, Successful: ${successful}`); console.log(` Total time: ${endTime - startTime}ms`); console.log(` DNS cache entries: ${dnsCache.size}`); console.log(` Expected ~${domains.length} DNS lookups for ${domains.length} domains`); smtpClient.close(); } finally { require('dns').lookup = originalLookup; testServer.close(); } }); // Scenario 3: DNS Cache TTL and Refresh await test.test('Scenario 3: DNS Cache TTL and Refresh', async () => { console.log('\nā° Testing DNS cache TTL and refresh behavior...'); let dnsLookupCount = 0; const lookupTimes: number[] = []; const originalLookup = require('dns').lookup; require('dns').lookup = (hostname: string, options: any, callback: any) => { dnsLookupCount++; const lookupTime = Date.now(); lookupTimes.push(lookupTime); console.log(` [DNS] Lookup ${dnsLookupCount} at ${new Date(lookupTime).toISOString().substr(11, 12)}`); setTimeout(() => { callback(null, '127.0.0.1', 4); }, 40); }; const testServer = await createTestServer({ responseDelay: 10 }); try { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 1, dnsCache: true, dnsCacheTtl: 1000 // 1 second TTL for testing }); console.log(' Sending emails with 1.5 second intervals to test TTL...'); const email1 = new Email({ from: 'sender@ttltest.com', to: ['recipient1@ttltest.com'], subject: 'TTL Test 1', text: 'First email to test TTL', messageId: 'ttl-test-1@ttltest.com' }); console.log(' Sending first email...'); await smtpClient.sendMail(email1); console.log(' āœ“ First email sent'); console.log(' Waiting 500ms (within TTL)...'); await new Promise(resolve => setTimeout(resolve, 500)); const email2 = new Email({ from: 'sender@ttltest.com', to: ['recipient2@ttltest.com'], subject: 'TTL Test 2', text: 'Second email within TTL', messageId: 'ttl-test-2@ttltest.com' }); console.log(' Sending second email (should use cache)...'); await smtpClient.sendMail(email2); console.log(' āœ“ Second email sent'); console.log(' Waiting 1000ms (TTL expiry)...'); await new Promise(resolve => setTimeout(resolve, 1000)); const email3 = new Email({ from: 'sender@ttltest.com', to: ['recipient3@ttltest.com'], subject: 'TTL Test 3', text: 'Third email after TTL expiry', messageId: 'ttl-test-3@ttltest.com' }); console.log(' Sending third email (should trigger new lookup)...'); await smtpClient.sendMail(email3); console.log(' āœ“ Third email sent'); console.log(` Total DNS lookups: ${dnsLookupCount}`); console.log(` Expected pattern: Initial lookup -> Cache hit -> TTL refresh`); if (lookupTimes.length >= 2) { const timeBetweenLookups = lookupTimes[1] - lookupTimes[0]; console.log(` Time between DNS lookups: ${timeBetweenLookups}ms`); console.log(` TTL behavior: ${timeBetweenLookups > 1000 ? 'Correct' : 'Needs review'}`); } smtpClient.close(); } finally { require('dns').lookup = originalLookup; testServer.close(); } }); // Scenario 4: DNS Cache Memory Management await test.test('Scenario 4: DNS Cache Memory Management', async () => { console.log('\nšŸ’¾ Testing DNS cache memory management and cleanup...'); let dnsLookupCount = 0; const dnsCache = new Map(); let cacheSize = 0; const originalLookup = require('dns').lookup; require('dns').lookup = (hostname: string, options: any, callback: any) => { dnsLookupCount++; if (!dnsCache.has(hostname)) { dnsCache.set(hostname, { ip: '127.0.0.1', timestamp: Date.now(), hits: 1 }); cacheSize++; console.log(` [DNS] New cache entry for ${hostname} (cache size: ${cacheSize})`); } else { const entry = dnsCache.get(hostname); entry.hits++; console.log(` [DNS] Cache hit for ${hostname} (hits: ${entry.hits})`); } setTimeout(() => { callback(null, '127.0.0.1', 4); }, 30); }; const testServer = await createTestServer({ responseDelay: 10 }); try { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2, dnsCache: true, dnsCacheTtl: 2000, dnsCacheSize: 5 // Small cache size for testing }); console.log(' Creating emails to many domains to test cache limits...'); const domains = []; for (let i = 0; i < 8; i++) { domains.push(`domain${i}.example.com`); } console.log(' Sending emails to test cache memory management...'); for (let i = 0; i < domains.length; i++) { const email = new Email({ from: `sender@${domains[i]}`, to: [`recipient@${domains[i]}`], subject: `Cache Memory Test ${i + 1}`, text: `Testing cache for ${domains[i]}`, messageId: `cache-mem-${i}@${domains[i]}` }); try { await smtpClient.sendMail(email); console.log(` āœ“ Email ${i + 1} to ${domains[i]} sent`); } catch (error) { console.log(` āœ— Email ${i + 1} failed: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 100)); } console.log(' Testing cache hit rates by resending to same domains...'); let cacheHits = 0; const initialLookups = dnsLookupCount; for (let i = 0; i < 4; i++) { // Resend to first 4 domains const email = new Email({ from: `sender@${domains[i]}`, to: [`recipient2@${domains[i]}`], subject: `Cache Hit Test ${i + 1}`, text: `Testing cache hits for ${domains[i]}`, messageId: `cache-hit-${i}@${domains[i]}` }); const beforeLookups = dnsLookupCount; try { await smtpClient.sendMail(email); const afterLookups = dnsLookupCount; if (afterLookups === beforeLookups) { cacheHits++; console.log(` āœ“ Cache hit for ${domains[i]}`); } else { console.log(` ⚔ Cache miss for ${domains[i]}`); } } catch (error) { console.log(` āœ— Email failed: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 50)); } const finalLookups = dnsLookupCount; console.log(` Total DNS lookups: ${finalLookups}`); console.log(` Unique domains tested: ${domains.length}`); console.log(` Cache entries created: ${cacheSize}`); console.log(` Cache hits on retests: ${cacheHits}/4`); console.log(` Cache efficiency: ${((cacheHits / 4) * 100).toFixed(1)}%`); console.log(` Memory management: ${cacheSize <= 5 ? 'Within limits' : 'Exceeded limits'}`); smtpClient.close(); } finally { require('dns').lookup = originalLookup; testServer.close(); } }); // Scenario 5: DNS Resolution Performance Impact await test.test('Scenario 5: DNS Resolution Performance Impact', async () => { console.log('\n⚔ Testing DNS resolution performance impact on email sending...'); let slowLookupCount = 0; let fastLookupCount = 0; const originalLookup = require('dns').lookup; // First test: Slow DNS responses console.log(' Phase 1: Testing with slow DNS responses (200ms delay)...'); require('dns').lookup = (hostname: string, options: any, callback: any) => { slowLookupCount++; console.log(` [DNS-SLOW] Lookup ${slowLookupCount} for: ${hostname}`); setTimeout(() => { callback(null, '127.0.0.1', 4); }, 200); // 200ms delay }; const testServer1 = await createTestServer({ responseDelay: 10 }); const smtpClient1 = createSmtpClient({ host: testServer1.hostname, port: testServer1.port, secure: false, pool: false, // No pooling to force DNS lookups dnsCache: false }); const emails1 = []; for (let i = 0; i < 3; i++) { emails1.push(new Email({ from: 'sender@slow.example.com', to: [`recipient${i}@slow.example.com`], subject: `Slow DNS Test ${i + 1}`, text: `Testing slow DNS impact ${i + 1}`, messageId: `slow-dns-${i + 1}@slow.example.com` })); } const slowStartTime = Date.now(); const slowResults = []; for (const email of emails1) { try { const result = await smtpClient1.sendMail(email); slowResults.push({ success: true }); console.log(` āœ“ Slow DNS email sent`); } catch (error) { slowResults.push({ success: false }); console.log(` āœ— Slow DNS email failed`); } } const slowEndTime = Date.now(); const slowTotalTime = slowEndTime - slowStartTime; smtpClient1.close(); testServer1.close(); // Second test: Fast DNS responses with caching console.log(' Phase 2: Testing with fast DNS responses and caching...'); require('dns').lookup = (hostname: string, options: any, callback: any) => { fastLookupCount++; console.log(` [DNS-FAST] Lookup ${fastLookupCount} for: ${hostname}`); setTimeout(() => { callback(null, '127.0.0.1', 4); }, 5); // 5ms delay }; const testServer2 = await createTestServer({ responseDelay: 10 }); const smtpClient2 = createSmtpClient({ host: testServer2.hostname, port: testServer2.port, secure: false, pool: true, dnsCache: true, dnsCacheTtl: 5000 }); const emails2 = []; for (let i = 0; i < 3; i++) { emails2.push(new Email({ from: 'sender@fast.example.com', to: [`recipient${i}@fast.example.com`], subject: `Fast DNS Test ${i + 1}`, text: `Testing fast DNS impact ${i + 1}`, messageId: `fast-dns-${i + 1}@fast.example.com` })); } const fastStartTime = Date.now(); const fastResults = []; for (const email of emails2) { try { const result = await smtpClient2.sendMail(email); fastResults.push({ success: true }); console.log(` āœ“ Fast DNS email sent`); } catch (error) { fastResults.push({ success: false }); console.log(` āœ— Fast DNS email failed`); } } const fastEndTime = Date.now(); const fastTotalTime = fastEndTime - fastStartTime; smtpClient2.close(); testServer2.close(); // Performance comparison const slowSuccess = slowResults.filter(r => r.success).length; const fastSuccess = fastResults.filter(r => r.success).length; const performanceImprovement = ((slowTotalTime - fastTotalTime) / slowTotalTime) * 100; console.log(` Slow DNS Results: ${slowTotalTime}ms, ${slowSuccess}/${emails1.length} successful`); console.log(` Fast DNS Results: ${fastTotalTime}ms, ${fastSuccess}/${emails2.length} successful`); console.log(` Performance improvement: ${performanceImprovement.toFixed(1)}%`); console.log(` DNS lookups - Slow: ${slowLookupCount}, Fast: ${fastLookupCount}`); console.log(` Caching efficiency: ${fastLookupCount < slowLookupCount ? 'Effective' : 'Needs improvement'}`); // Restore original DNS lookup require('dns').lookup = originalLookup; }); console.log('\nāœ… CPERF-08: DNS Caching Efficiency Performance Tests completed'); console.log('🌐 All DNS caching scenarios tested successfully'); });