Files
smartmta/test/test.email.contracts.node.ts

123 lines
3.8 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { Email } from '../ts/mail/core/classes.email.js';
import { DKIMCreator } from '../ts/mail/security/classes.dkimcreator.js';
import { UnifiedEmailServer } from '../ts/mail/routing/classes.unified.email.server.js';
const storageMap = new Map<string, string>();
const serversToCleanup: UnifiedEmailServer[] = [];
const mockDcRouter = {
storageManager: {
get: async (key: string) => storageMap.get(key) || null,
set: async (key: string, value: string) => {
storageMap.set(key, value);
},
list: async (prefix: string) => Array.from(storageMap.keys()).filter((key) => key.startsWith(prefix)),
delete: async (key: string) => {
storageMap.delete(key);
},
},
};
tap.test('UnifiedEmailServer.sendEmail returns the actual queue item id', async () => {
const server = new UnifiedEmailServer(mockDcRouter, {
ports: [10025],
hostname: 'mail.example.com',
domains: [{ domain: 'example.com', dnsMode: 'forward' }],
routes: [],
});
serversToCleanup.push(server);
const route = {
name: 'test-deliver-route',
match: { recipients: '*@*' },
action: { type: 'deliver' as const },
};
const email = new Email({
from: 'sender@example.com',
to: ['recipient@example.net'],
subject: 'Queue ID contract',
text: 'hello',
});
const queueId = await server.sendEmail(email, 'mta', route);
const queuedItem = server.getQueueItem(queueId);
expect(queuedItem).toBeTruthy();
expect(queuedItem?.id).toEqual(queueId);
expect(server.getQueueStats().queueSize).toEqual(1);
expect(server.getQueueItems().map((item) => item.id)).toContain(queueId);
});
tap.test('UnifiedEmailServer.sendOutboundEmail uses outbound.hostname when configured', async () => {
const server = new UnifiedEmailServer(mockDcRouter, {
ports: [10026],
hostname: 'mail.example.com',
outbound: {
hostname: 'outbound.example.com',
},
domains: [{ domain: 'example.com', dnsMode: 'forward' }],
routes: [],
});
serversToCleanup.push(server);
const email = new Email({
from: 'sender@example.com',
to: ['recipient@example.net'],
subject: 'Outbound hostname contract',
text: 'hello',
});
let capturedOptions: any;
(server as any).rustBridge.sendOutboundEmail = async (options: any) => {
capturedOptions = options;
return {
accepted: ['recipient@example.net'],
rejected: [],
messageId: 'test-message-id',
response: '250 2.0.0 queued',
envelope: {
from: 'sender@example.com',
to: ['recipient@example.net'],
},
};
};
await server.sendOutboundEmail('smtp.target.example', 25, email);
expect(capturedOptions).toBeTruthy();
expect(capturedOptions.domain).toEqual('outbound.example.com');
});
tap.test('DKIMCreator returns selector-aligned DNS record names', async () => {
const tempDir = path.join(os.tmpdir(), `smartmta-dkim-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
try {
const creator = new DKIMCreator(tempDir);
await creator.createAndStoreDKIMKeys('example.com');
const defaultRecord = await creator.getDNSRecordForDomain('example.com');
expect(defaultRecord.name).toEqual('default._domainkey.example.com');
await creator.createAndStoreDKIMKeysForSelector('example.org', 'selector1');
const selectorRecord = await creator.getDNSRecordForDomain('example.org', 'selector1');
expect(selectorRecord.name).toEqual('selector1._domainkey.example.org');
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
tap.test('cleanup', async () => {
for (const server of serversToCleanup) {
await server.stop();
}
await tap.stopForcefully();
});
export default tap.start();