dcrouter/test/suite/smtpclient_connection/test.ccm-09.ipv6-dual-stack.ts
2025-05-25 19:05:43 +00:00

167 lines
4.7 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
import * as net from 'net';
import * as os from 'os';
let testServer: ITestServer;
tap.test('setup test SMTP server', async () => {
testServer = await startTestServer({
port: 2535,
tlsEnabled: false,
authRequired: false
});
expect(testServer).toBeTruthy();
expect(testServer.port).toEqual(2535);
});
tap.test('CCM-09: Check system IPv6 support', async () => {
const networkInterfaces = os.networkInterfaces();
let hasIPv6 = false;
for (const interfaceName in networkInterfaces) {
const interfaces = networkInterfaces[interfaceName];
if (interfaces) {
for (const iface of interfaces) {
if (iface.family === 'IPv6' && !iface.internal) {
hasIPv6 = true;
console.log(`Found IPv6 address: ${iface.address} on ${interfaceName}`);
}
}
}
}
console.log(`System has IPv6 support: ${hasIPv6}`);
});
tap.test('CCM-09: IPv4 connection test', async () => {
const smtpClient = createSmtpClient({
host: '127.0.0.1', // Explicit IPv4
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Test connection using verify
const verified = await smtpClient.verify();
expect(verified).toBeTrue();
console.log('Successfully connected via IPv4');
await smtpClient.close();
});
tap.test('CCM-09: IPv6 connection test (if supported)', async () => {
// Check if IPv6 is available
const hasIPv6 = await new Promise<boolean>((resolve) => {
const testSocket = net.createConnection({
host: '::1',
port: 1, // Any port, will fail but tells us if IPv6 works
timeout: 100
});
testSocket.on('error', (err: any) => {
// ECONNREFUSED means IPv6 works but port is closed (expected)
// ENETUNREACH or EAFNOSUPPORT means IPv6 not available
resolve(err.code === 'ECONNREFUSED');
});
testSocket.on('connect', () => {
testSocket.end();
resolve(true);
});
});
if (!hasIPv6) {
console.log('IPv6 not available on this system, skipping IPv6 tests');
return;
}
// Try IPv6 connection
const smtpClient = createSmtpClient({
host: '::1', // IPv6 loopback
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
try {
const verified = await smtpClient.verify();
if (verified) {
console.log('Successfully connected via IPv6');
await smtpClient.close();
} else {
console.log('IPv6 connection failed (server may not support IPv6)');
}
} catch (error: any) {
console.log('IPv6 connection failed (server may not support IPv6):', error.message);
}
});
tap.test('CCM-09: Hostname resolution preference', async () => {
// Test that client can handle hostnames that resolve to both IPv4 and IPv6
const smtpClient = createSmtpClient({
host: 'localhost', // Should resolve to both 127.0.0.1 and ::1
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
const verified = await smtpClient.verify();
expect(verified).toBeTrue();
console.log('Successfully connected to localhost');
await smtpClient.close();
});
tap.test('CCM-09: Happy Eyeballs algorithm simulation', async () => {
// Test connecting to multiple addresses with preference
const addresses = ['127.0.0.1', '::1', 'localhost'];
const results: Array<{ address: string; time: number; success: boolean }> = [];
for (const address of addresses) {
const startTime = Date.now();
const smtpClient = createSmtpClient({
host: address,
port: testServer.port,
secure: false,
connectionTimeout: 1000,
debug: false
});
try {
const verified = await smtpClient.verify();
const elapsed = Date.now() - startTime;
results.push({ address, time: elapsed, success: verified });
if (verified) {
await smtpClient.close();
}
} catch (error) {
const elapsed = Date.now() - startTime;
results.push({ address, time: elapsed, success: false });
}
}
console.log('Connection race results:');
results.forEach(r => {
console.log(` ${r.address}: ${r.success ? 'SUCCESS' : 'FAILED'} in ${r.time}ms`);
});
// At least one should succeed
const successfulConnections = results.filter(r => r.success);
expect(successfulConnections.length).toBeGreaterThan(0);
});
tap.test('cleanup test SMTP server', async () => {
if (testServer) {
await stopTestServer(testServer);
}
});
export default tap.start();