fix(mail): align queue, outbound hostname, and DKIM selector behavior across the mail server APIs
This commit is contained in:
122
test/test.email.contracts.node.ts
Normal file
122
test/test.email.contracts.node.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user