import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from './plugins.js'; import { createTestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; tap.test('CPERF-06: should implement efficient caching strategies', async (tools) => { const testId = 'CPERF-06-caching-strategies'; console.log(`\n${testId}: Testing caching strategies performance...`); let scenarioCount = 0; // Scenario 1: DNS resolution caching await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing DNS resolution caching`); let dnsLookupCount = 0; const dnsCache = new Map(); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 dns-cache.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-dns-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Simulate DNS lookup caching const mockDnsLookup = (hostname: string) => { const cached = dnsCache.get(hostname); const now = Date.now(); if (cached && (now - cached.timestamp) < 300000) { // 5 minute cache console.log(` [DNS] Cache hit for ${hostname}`); return cached.address; } dnsLookupCount++; console.log(` [DNS] Cache miss for ${hostname} (lookup #${dnsLookupCount})`); const address = testServer.hostname; // Mock resolution dnsCache.set(hostname, { address, timestamp: now }); return address; }; // Test multiple connections to same host const connectionCount = 10; console.log(` Creating ${connectionCount} connections to test DNS caching...`); const clients: any[] = []; const startTime = Date.now(); for (let i = 0; i < connectionCount; i++) { // Simulate DNS lookup const resolvedHost = mockDnsLookup(testServer.hostname); const client = createSmtpClient({ host: resolvedHost, port: testServer.port, secure: false }); clients.push(client); } // Send emails through cached connections const emails = clients.map((client, i) => new plugins.smartmail.Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: `DNS cache test ${i + 1}`, text: `Testing DNS resolution caching - connection ${i + 1}` }) ); await Promise.all(emails.map((email, i) => clients[i].sendMail(email))); const totalTime = Date.now() - startTime; const cacheHitRate = ((connectionCount - dnsLookupCount) / connectionCount) * 100; console.log(` DNS lookups performed: ${dnsLookupCount}/${connectionCount}`); console.log(` Cache hit rate: ${cacheHitRate.toFixed(1)}%`); console.log(` Total time: ${totalTime}ms`); console.log(` Time per connection: ${(totalTime / connectionCount).toFixed(1)}ms`); // Close clients await Promise.all(clients.map(client => { if (client.close) { return client.close(); } return Promise.resolve(); })); // DNS caching should reduce lookups expect(dnsLookupCount).toBeLessThan(connectionCount); expect(cacheHitRate).toBeGreaterThan(50); // At least 50% cache hit rate await testServer.server.close(); })(); // Scenario 2: Connection pool caching await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing connection pool caching`); let connectionCount = 0; let connectionReuse = 0; const connectionPool = new Map(); const testServer = await createTestServer({ onConnection: async (socket) => { connectionCount++; const connId = connectionCount; console.log(` [Server] New connection ${connId} created`); socket.write('220 pool-cache.example.com ESMTP\r\n'); let messageCount = 0; socket.on('close', () => { console.log(` [Server] Connection ${connId} closed after ${messageCount} messages`); }); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-pool-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { messageCount++; socket.write(`250 OK: Message ${messageCount} on connection ${connId}\r\n`); } else if (command === 'RSET') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Mock connection pool management const getPooledConnection = (key: string) => { const cached = connectionPool.get(key); const now = Date.now(); if (cached && (now - cached.lastUsed) < 60000) { // 1 minute idle timeout connectionReuse++; cached.lastUsed = now; cached.messageCount++; console.log(` [Pool] Reusing connection for ${key} (reuse #${connectionReuse})`); return cached.connection; } console.log(` [Pool] Creating new connection for ${key}`); const newConnection = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 5, maxMessages: 10 }); connectionPool.set(key, { connection: newConnection, lastUsed: now, messageCount: 0 }); return newConnection; }; // Test connection reuse with same destination const destinations = [ 'example.com', 'example.com', // Same as first (should reuse) 'example.com', // Same as first (should reuse) 'another.com', 'example.com', // Back to first (should reuse) 'another.com' // Same as fourth (should reuse) ]; console.log(` Sending emails to test connection pool caching...`); for (let i = 0; i < destinations.length; i++) { const destination = destinations[i]; const poolKey = destination; const client = getPooledConnection(poolKey); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: [`recipient${i + 1}@${destination}`], subject: `Pool cache test ${i + 1}`, text: `Testing connection pool caching - destination ${destination}` }); await client.sendMail(email); } // Close all pooled connections for (const [key, pooled] of connectionPool) { if (pooled.connection.close) { await pooled.connection.close(); } } const uniqueDestinations = new Set(destinations).size; const poolEfficiency = (connectionReuse / destinations.length) * 100; console.log(` Total emails sent: ${destinations.length}`); console.log(` Unique destinations: ${uniqueDestinations}`); console.log(` New connections: ${connectionCount}`); console.log(` Connection reuses: ${connectionReuse}`); console.log(` Pool efficiency: ${poolEfficiency.toFixed(1)}%`); // Connection pool should reuse connections efficiently expect(connectionCount).toBeLessThanOrEqual(uniqueDestinations + 1); expect(connectionReuse).toBeGreaterThan(0); expect(poolEfficiency).toBeGreaterThan(30); // At least 30% reuse await testServer.server.close(); })(); // Scenario 3: Template and content caching await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing template and content caching`); let templateCompilations = 0; let cacheHits = 0; const templateCache = new Map(); const testServer = await createTestServer({ onConnection: async (socket) => { socket.write('220 template-cache.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-template-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Mock template compilation and caching const compileTemplate = (template: string, data: any) => { const cacheKey = template; const cached = templateCache.get(cacheKey); const now = Date.now(); if (cached && (now - cached.timestamp) < 3600000) { // 1 hour cache cacheHits++; cached.uses++; console.log(` [Template] Cache hit for template (use #${cached.uses})`); return cached.compiled.replace(/\{\{(\w+)\}\}/g, (match, key) => data[key] || match); } templateCompilations++; console.log(` [Template] Compiling template (compilation #${templateCompilations})`); // Simulate template compilation overhead const compiled = template.replace(/\{\{(\w+)\}\}/g, (match, key) => data[key] || match); templateCache.set(cacheKey, { compiled: template, // Store template for reuse timestamp: now, uses: 1 }); return compiled; }; const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test template caching with repeated templates const templates = [ { id: 'welcome', subject: 'Welcome {{name}}!', text: 'Hello {{name}}, welcome to our service!' }, { id: 'notification', subject: 'Notification for {{name}}', text: 'Dear {{name}}, you have a new notification.' }, { id: 'welcome', // Repeat of first template subject: 'Welcome {{name}}!', text: 'Hello {{name}}, welcome to our service!' } ]; const users = [ { name: 'Alice', email: 'alice@example.com' }, { name: 'Bob', email: 'bob@example.com' }, { name: 'Charlie', email: 'charlie@example.com' }, { name: 'Diana', email: 'diana@example.com' } ]; console.log(' Sending templated emails to test content caching...'); const startTime = Date.now(); for (const user of users) { for (const template of templates) { const compiledSubject = compileTemplate(template.subject, user); const compiledText = compileTemplate(template.text, user); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: [user.email], subject: compiledSubject, text: compiledText }); await smtpClient.sendMail(email); } } const totalTime = Date.now() - startTime; const totalTemplateUses = users.length * templates.length; const uniqueTemplates = new Set(templates.map(t => t.id)).size; const cacheEfficiency = (cacheHits / (templateCompilations + cacheHits)) * 100; console.log(` Total template uses: ${totalTemplateUses}`); console.log(` Unique templates: ${uniqueTemplates}`); console.log(` Template compilations: ${templateCompilations}`); console.log(` Cache hits: ${cacheHits}`); console.log(` Cache efficiency: ${cacheEfficiency.toFixed(1)}%`); console.log(` Average time per email: ${(totalTime / totalTemplateUses).toFixed(1)}ms`); // Template caching should reduce compilation overhead expect(templateCompilations).toBeLessThan(totalTemplateUses); expect(cacheHits).toBeGreaterThan(0); expect(cacheEfficiency).toBeGreaterThan(50); // At least 50% cache efficiency await testServer.server.close(); })(); // Scenario 4: Message header caching await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing message header caching`); let headerGenerations = 0; let headerCacheHits = 0; const headerCache = new Map(); const testServer = await createTestServer({ onConnection: async (socket) => { socket.write('220 header-cache.example.com ESMTP\r\n'); let messageCount = 0; socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-header-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { messageCount++; socket.write(`250 OK: Message ${messageCount} with cached headers\r\n`); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Mock header generation and caching const generateHeaders = (from: string, subject: string, messageType: string) => { const cacheKey = `${from}-${messageType}`; const cached = headerCache.get(cacheKey); const now = Date.now(); if (cached && (now - cached.timestamp) < 1800000) { // 30 minute cache headerCacheHits++; console.log(` [Headers] Cache hit for ${messageType} headers`); return { ...cached.headers, Subject: subject, // Subject is dynamic Date: new Date().toISOString(), 'Message-ID': `<${Date.now()}-${Math.random()}@example.com>` }; } headerGenerations++; console.log(` [Headers] Generating ${messageType} headers (generation #${headerGenerations})`); // Simulate header generation overhead const headers = { From: from, Subject: subject, Date: new Date().toISOString(), 'Message-ID': `<${Date.now()}-${Math.random()}@example.com>`, 'X-Mailer': 'Test Mailer 1.0', 'MIME-Version': '1.0', 'Content-Type': messageType === 'html' ? 'text/html; charset=UTF-8' : 'text/plain; charset=UTF-8' }; // Cache the static parts const cacheableHeaders = { From: from, 'X-Mailer': 'Test Mailer 1.0', 'MIME-Version': '1.0', 'Content-Type': headers['Content-Type'] }; headerCache.set(cacheKey, { headers: cacheableHeaders, timestamp: now }); return headers; }; const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test header caching with similar message types const messageTypes = ['text', 'html', 'text', 'html', 'text']; // Repeated types const sender = 'sender@example.com'; console.log(' Sending emails to test header caching...'); for (let i = 0; i < messageTypes.length; i++) { const messageType = messageTypes[i]; const headers = generateHeaders(sender, `Test ${i + 1}`, messageType); const email = new plugins.smartmail.Email({ from: headers.From, to: [`recipient${i + 1}@example.com`], subject: headers.Subject, text: messageType === 'text' ? 'Plain text message' : undefined, html: messageType === 'html' ? '

HTML message

' : undefined, headers: { 'X-Mailer': headers['X-Mailer'], 'Message-ID': headers['Message-ID'] } }); await smtpClient.sendMail(email); } const uniqueMessageTypes = new Set(messageTypes).size; const headerCacheEfficiency = (headerCacheHits / (headerGenerations + headerCacheHits)) * 100; console.log(` Messages sent: ${messageTypes.length}`); console.log(` Unique message types: ${uniqueMessageTypes}`); console.log(` Header generations: ${headerGenerations}`); console.log(` Header cache hits: ${headerCacheHits}`); console.log(` Header cache efficiency: ${headerCacheEfficiency.toFixed(1)}%`); // Header caching should reduce generation overhead expect(headerGenerations).toBeLessThan(messageTypes.length); expect(headerCacheHits).toBeGreaterThan(0); await testServer.server.close(); })(); // Scenario 5: Attachment processing caching await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing attachment processing caching`); let attachmentProcessing = 0; let attachmentCacheHits = 0; const attachmentCache = new Map(); const testServer = await createTestServer({ onConnection: async (socket) => { socket.write('220 attachment-cache.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-attachment-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Mock attachment processing with caching const processAttachment = (filename: string, content: Buffer) => { const contentHash = require('crypto').createHash('md5').update(content).digest('hex'); const cacheKey = `${filename}-${contentHash}`; const cached = attachmentCache.get(cacheKey); const now = Date.now(); if (cached && (now - cached.timestamp) < 7200000) { // 2 hour cache attachmentCacheHits++; console.log(` [Attachment] Cache hit for ${filename}`); return cached.processed; } attachmentProcessing++; console.log(` [Attachment] Processing ${filename} (processing #${attachmentProcessing})`); // Simulate attachment processing (base64 encoding) const processed = content.toString('base64'); attachmentCache.set(cacheKey, { processed, timestamp: now, size: content.length }); return processed; }; const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Create reusable attachment content const commonAttachment = Buffer.from('This is a common attachment used in multiple emails.'); const uniqueAttachment = Buffer.from('This is a unique attachment.'); const emails = [ { subject: 'Email 1 with common attachment', attachments: [{ filename: 'common.txt', content: commonAttachment }] }, { subject: 'Email 2 with unique attachment', attachments: [{ filename: 'unique.txt', content: uniqueAttachment }] }, { subject: 'Email 3 with common attachment again', attachments: [{ filename: 'common.txt', content: commonAttachment }] // Same as first }, { subject: 'Email 4 with both attachments', attachments: [ { filename: 'common.txt', content: commonAttachment }, { filename: 'unique.txt', content: uniqueAttachment } ] } ]; console.log(' Sending emails with attachments to test caching...'); for (let i = 0; i < emails.length; i++) { const emailData = emails[i]; // Process attachments (with caching) const processedAttachments = emailData.attachments.map(att => ({ filename: att.filename, content: processAttachment(att.filename, att.content) })); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: emailData.subject, text: 'Email with attachments for caching test', attachments: processedAttachments.map(att => ({ filename: att.filename, content: att.content, encoding: 'base64' as const })) }); await smtpClient.sendMail(email); } const totalAttachments = emails.reduce((sum, email) => sum + email.attachments.length, 0); const attachmentCacheEfficiency = (attachmentCacheHits / (attachmentProcessing + attachmentCacheHits)) * 100; console.log(` Total attachments sent: ${totalAttachments}`); console.log(` Attachment processing operations: ${attachmentProcessing}`); console.log(` Attachment cache hits: ${attachmentCacheHits}`); console.log(` Attachment cache efficiency: ${attachmentCacheEfficiency.toFixed(1)}%`); // Attachment caching should reduce processing overhead expect(attachmentProcessing).toBeLessThan(totalAttachments); expect(attachmentCacheHits).toBeGreaterThan(0); expect(attachmentCacheEfficiency).toBeGreaterThan(30); // At least 30% cache efficiency await testServer.server.close(); })(); // Scenario 6: Overall caching performance impact await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing overall caching performance impact`); const testServer = await createTestServer({ onConnection: async (socket) => { socket.write('220 performance-cache.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-performance-cache.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Test performance with caching enabled vs disabled const emailCount = 20; // Simulate no caching (always process) console.log(' Testing performance without caching...'); const noCacheStart = Date.now(); let noCacheOperations = 0; const noCacheClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); for (let i = 0; i < emailCount; i++) { // Simulate processing overhead for each email noCacheOperations += 3; // DNS lookup, header generation, template processing const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: [`nocache${i + 1}@example.com`], subject: `No cache test ${i + 1}`, text: `Testing performance without caching - email ${i + 1}` }); await noCacheClient.sendMail(email); } const noCacheTime = Date.now() - noCacheStart; // Simulate with caching (reduced processing) console.log(' Testing performance with caching...'); const cacheStart = Date.now(); let cacheOperations = 5; // Initial setup, then reuse const cacheClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2 }); for (let i = 0; i < emailCount; i++) { // Simulate reduced operations due to caching if (i < 5) { cacheOperations += 1; // Some cache misses initially } // Most operations are cache hits (no additional operations) const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: [`cache${i + 1}@example.com`], subject: `Cache test ${i + 1}`, text: `Testing performance with caching - email ${i + 1}` }); await cacheClient.sendMail(email); } await cacheClient.close(); const cacheTime = Date.now() - cacheStart; // Calculate performance improvements const timeImprovement = ((noCacheTime - cacheTime) / noCacheTime) * 100; const operationReduction = ((noCacheOperations - cacheOperations) / noCacheOperations) * 100; const throughputImprovement = (emailCount / cacheTime) / (emailCount / noCacheTime); console.log(` Performance comparison (${emailCount} emails):`); console.log(` Without caching: ${noCacheTime}ms, ${noCacheOperations} operations`); console.log(` With caching: ${cacheTime}ms, ${cacheOperations} operations`); console.log(` Time improvement: ${timeImprovement.toFixed(1)}%`); console.log(` Operation reduction: ${operationReduction.toFixed(1)}%`); console.log(` Throughput improvement: ${throughputImprovement.toFixed(2)}x`); // Caching should improve performance expect(cacheTime).toBeLessThan(noCacheTime); expect(cacheOperations).toBeLessThan(noCacheOperations); expect(timeImprovement).toBeGreaterThan(10); // At least 10% improvement expect(throughputImprovement).toBeGreaterThan(1.1); // At least 10% better throughput await testServer.server.close(); })(); console.log(`\n${testId}: All ${scenarioCount} caching strategy scenarios tested ✓`); });