243 lines
9.1 KiB
TypeScript
243 lines
9.1 KiB
TypeScript
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)) {
|
|
// Remove the directory recursively using fs instead of smartfile
|
|
plugins.fs.rmSync(reputationDataPath, { recursive: true, force: true });
|
|
}
|
|
};
|
|
|
|
// 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).toBeTruthy();
|
|
// Check if the object has the expected methods
|
|
expect(typeof reputationMonitor.recordSendEvent).toEqual('function');
|
|
expect(typeof reputationMonitor.getReputationData).toEqual('function');
|
|
expect(typeof reputationMonitor.getReputationSummary).toEqual('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).toBeTruthy();
|
|
expect(data.domain).toEqual('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).toBeTruthy();
|
|
expect(metrics.volume.sent).toEqual(100);
|
|
expect(metrics.volume.delivered).toEqual(95);
|
|
expect(metrics.volume.hardBounces).toEqual(3);
|
|
expect(metrics.volume.softBounces).toEqual(2);
|
|
expect(metrics.complaints.total).toEqual(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(Array.isArray(summary)).toBeTrue();
|
|
expect(summary.length >= 3).toBeTrue();
|
|
|
|
// Check that domains are included in the summary
|
|
const domains = summary.map(item => item.domain);
|
|
expect(domains.includes('high.com')).toBeTrue();
|
|
expect(domains.includes('medium.com')).toBeTrue();
|
|
expect(domains.includes('low.com')).toBeTrue();
|
|
});
|
|
|
|
// 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).toBeTruthy();
|
|
expect(metrics.volume.sent).toEqual(50);
|
|
|
|
// Remove a domain
|
|
reputationMonitor.removeDomain('newdomain.com');
|
|
|
|
// Check that data is no longer available
|
|
const removedMetrics = reputationMonitor.getReputationData('newdomain.com');
|
|
expect(removedMetrics === null).toBeTrue();
|
|
});
|
|
|
|
// 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).toBeTruthy();
|
|
expect(metrics.engagement.opens).toEqual(500);
|
|
expect(metrics.engagement.clicks).toEqual(250);
|
|
expect(typeof metrics.engagement.openRate).toEqual('number');
|
|
expect(typeof metrics.engagement.clickRate).toEqual('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).toBeTruthy();
|
|
expect(metrics.historical.reputationScores).toBeTruthy();
|
|
|
|
// Check that daily send volume is tracked
|
|
expect(metrics.volume.dailySendVolume).toBeTruthy();
|
|
const todayStr = today.toISOString().split('T')[0];
|
|
expect(metrics.volume.dailySendVolume[todayStr]).toEqual(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).toEqual(100);
|
|
expect(metrics.volume.delivered).toEqual(95);
|
|
expect(metrics.volume.hardBounces).toEqual(3);
|
|
expect(metrics.volume.softBounces).toEqual(2);
|
|
|
|
// Check complaint metrics
|
|
expect(metrics.complaints.total).toEqual(1);
|
|
expect(metrics.complaints.topDomains[0].domain).toEqual('gmail.com');
|
|
|
|
// Check engagement metrics
|
|
expect(metrics.engagement.opens).toEqual(50);
|
|
expect(metrics.engagement.clicks).toEqual(25);
|
|
});
|
|
|
|
// After all tests, clean up
|
|
tap.test('cleanup', async () => {
|
|
cleanupTestData();
|
|
});
|
|
|
|
tap.test('stop', async () => {
|
|
await tap.stopForcefully();
|
|
});
|
|
|
|
export default tap.start(); |