import { tap, expect } from '@push.rocks/tapbundle'; import * as plugins from '../ts/plugins.js'; import * as paths from '../ts/paths.js'; import { SenderReputationMonitor } from '../ts/deliverability/classes.senderreputationmonitor.js'; // Cleanup any temporary test data const cleanupTestData = () => { const reputationDataPath = plugins.path.join(paths.dataDir, 'reputation'); if (plugins.fs.existsSync(reputationDataPath)) { plugins.smartfile.memory.unlinkDir(reputationDataPath); } }; // Helper to reset the singleton instance between tests const resetSingleton = () => { // @ts-ignore - accessing private static field for testing SenderReputationMonitor._instance = null; }; // Before running any tests tap.test('setup', async () => { cleanupTestData(); }); // Test initialization of SenderReputationMonitor tap.test('should initialize SenderReputationMonitor with default settings', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance(); expect(reputationMonitor).to.be.an('object'); // Check if the object has the expected methods expect(reputationMonitor.recordSendEvent).to.be.a('function'); expect(reputationMonitor.getReputationData).to.be.a('function'); expect(reputationMonitor.getReputationSummary).to.be.a('function'); }); // Test initialization with custom settings tap.test('should initialize SenderReputationMonitor with custom settings', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com', 'test.com'], updateFrequency: 12 * 60 * 60 * 1000, // 12 hours alertThresholds: { minReputationScore: 80, maxComplaintRate: 0.05 } }); // Test adding domains reputationMonitor.addDomain('newdomain.com'); // Test retrieving reputation data const data = reputationMonitor.getReputationData('example.com'); expect(data).to.be.an('object'); expect(data.domain).to.equal('example.com'); }); // Test recording and tracking send events tap.test('should record send events and update metrics', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com'] }); // Record a series of events reputationMonitor.recordSendEvent('example.com', { type: 'sent', count: 100 }); reputationMonitor.recordSendEvent('example.com', { type: 'delivered', count: 95 }); reputationMonitor.recordSendEvent('example.com', { type: 'bounce', hardBounce: true, count: 3 }); reputationMonitor.recordSendEvent('example.com', { type: 'bounce', hardBounce: false, count: 2 }); reputationMonitor.recordSendEvent('example.com', { type: 'complaint', count: 1 }); // Check metrics const metrics = reputationMonitor.getReputationData('example.com'); expect(metrics).to.be.an('object'); expect(metrics.volume.sent).to.equal(100); expect(metrics.volume.delivered).to.equal(95); expect(metrics.volume.hardBounces).to.equal(3); expect(metrics.volume.softBounces).to.equal(2); expect(metrics.complaints.total).to.equal(1); }); // Test reputation score calculation tap.test('should calculate reputation scores correctly', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['high.com', 'medium.com', 'low.com'] }); // Record events for different domains reputationMonitor.recordSendEvent('high.com', { type: 'sent', count: 1000 }); reputationMonitor.recordSendEvent('high.com', { type: 'delivered', count: 990 }); reputationMonitor.recordSendEvent('high.com', { type: 'open', count: 500 }); reputationMonitor.recordSendEvent('medium.com', { type: 'sent', count: 1000 }); reputationMonitor.recordSendEvent('medium.com', { type: 'delivered', count: 950 }); reputationMonitor.recordSendEvent('medium.com', { type: 'open', count: 300 }); reputationMonitor.recordSendEvent('low.com', { type: 'sent', count: 1000 }); reputationMonitor.recordSendEvent('low.com', { type: 'delivered', count: 850 }); reputationMonitor.recordSendEvent('low.com', { type: 'open', count: 100 }); // Get reputation summary const summary = reputationMonitor.getReputationSummary(); expect(summary).to.be.an('array'); expect(summary.length).to.be.at.least(3); // Check that domains are included in the summary const domains = summary.map(item => item.domain); expect(domains).to.include('high.com'); expect(domains).to.include('medium.com'); expect(domains).to.include('low.com'); }); // Test adding and removing domains tap.test('should add and remove domains for monitoring', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com'] }); // Add a new domain reputationMonitor.addDomain('newdomain.com'); // Record data for the new domain reputationMonitor.recordSendEvent('newdomain.com', { type: 'sent', count: 50 }); // Check that data was recorded for the new domain const metrics = reputationMonitor.getReputationData('newdomain.com'); expect(metrics).to.be.an('object'); expect(metrics.volume.sent).to.equal(50); // Remove a domain reputationMonitor.removeDomain('newdomain.com'); // Check that data is no longer available const removedMetrics = reputationMonitor.getReputationData('newdomain.com'); expect(removedMetrics).to.be.null; }); // Test handling open and click events tap.test('should track engagement metrics correctly', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com'] }); // Record basic sending metrics reputationMonitor.recordSendEvent('example.com', { type: 'sent', count: 1000 }); reputationMonitor.recordSendEvent('example.com', { type: 'delivered', count: 950 }); // Record engagement events reputationMonitor.recordSendEvent('example.com', { type: 'open', count: 500 }); reputationMonitor.recordSendEvent('example.com', { type: 'click', count: 250 }); // Check engagement metrics const metrics = reputationMonitor.getReputationData('example.com'); expect(metrics).to.be.an('object'); expect(metrics.engagement.opens).to.equal(500); expect(metrics.engagement.clicks).to.equal(250); expect(metrics.engagement.openRate).to.be.a('number'); expect(metrics.engagement.clickRate).to.be.a('number'); }); // Test historical data tracking tap.test('should store historical reputation data', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com'] }); // Record events over multiple days const today = new Date(); // Record data reputationMonitor.recordSendEvent('example.com', { type: 'sent', count: 1000 }); reputationMonitor.recordSendEvent('example.com', { type: 'delivered', count: 950 }); // Get metrics data const metrics = reputationMonitor.getReputationData('example.com'); // Check that historical data exists expect(metrics.historical).to.be.an('object'); expect(metrics.historical.reputationScores).to.be.an('object'); // Check that daily send volume is tracked expect(metrics.volume.dailySendVolume).to.be.an('object'); const todayStr = today.toISOString().split('T')[0]; expect(metrics.volume.dailySendVolume[todayStr]).to.equal(1000); }); // Test event recording for different event types tap.test('should correctly handle different event types', async () => { resetSingleton(); const reputationMonitor = SenderReputationMonitor.getInstance({ enabled: true, domains: ['example.com'] }); // Record different types of events reputationMonitor.recordSendEvent('example.com', { type: 'sent', count: 100 }); reputationMonitor.recordSendEvent('example.com', { type: 'delivered', count: 95 }); reputationMonitor.recordSendEvent('example.com', { type: 'bounce', hardBounce: true, count: 3 }); reputationMonitor.recordSendEvent('example.com', { type: 'bounce', hardBounce: false, count: 2 }); reputationMonitor.recordSendEvent('example.com', { type: 'complaint', receivingDomain: 'gmail.com', count: 1 }); reputationMonitor.recordSendEvent('example.com', { type: 'open', count: 50 }); reputationMonitor.recordSendEvent('example.com', { type: 'click', count: 25 }); // Check metrics for different event types const metrics = reputationMonitor.getReputationData('example.com'); // Check volume metrics expect(metrics.volume.sent).to.equal(100); expect(metrics.volume.delivered).to.equal(95); expect(metrics.volume.hardBounces).to.equal(3); expect(metrics.volume.softBounces).to.equal(2); // Check complaint metrics expect(metrics.complaints.total).to.equal(1); expect(metrics.complaints.topDomains[0].domain).to.equal('gmail.com'); // Check engagement metrics expect(metrics.engagement.opens).to.equal(50); expect(metrics.engagement.clicks).to.equal(25); }); // After all tests, clean up tap.test('cleanup', async () => { cleanupTestData(); }); export default tap.start();