168 lines
5.1 KiB
TypeScript
168 lines
5.1 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 type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js';
|
|
|
|
let testServer: ITestServer;
|
|
let smtpClient: SmtpClient;
|
|
|
|
tap.test('setup - start SMTP server for command tests', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2540,
|
|
tlsEnabled: false,
|
|
authRequired: false
|
|
});
|
|
|
|
expect(testServer.port).toEqual(2540);
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should send EHLO with custom domain', async () => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
// Create SMTP client with custom domain
|
|
smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
domain: 'mail.example.com', // Custom EHLO domain
|
|
connectionTimeout: 5000,
|
|
debug: true
|
|
});
|
|
|
|
// Verify connection (which sends EHLO)
|
|
const isConnected = await smtpClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
|
|
const duration = Date.now() - startTime;
|
|
console.log(`✅ EHLO command sent with custom domain in ${duration}ms`);
|
|
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
console.error(`❌ EHLO command failed after ${duration}ms:`, error);
|
|
throw error;
|
|
}
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should use default domain when not specified', async () => {
|
|
const defaultClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
// No domain specified - should use default
|
|
connectionTimeout: 5000,
|
|
debug: true
|
|
});
|
|
|
|
const isConnected = await defaultClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
|
|
await defaultClient.close();
|
|
console.log('✅ EHLO sent with default domain');
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should handle international domains', async () => {
|
|
const intlClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
domain: 'mail.例え.jp', // International domain
|
|
connectionTimeout: 5000,
|
|
debug: true
|
|
});
|
|
|
|
const isConnected = await intlClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
|
|
await intlClient.close();
|
|
console.log('✅ EHLO sent with international domain');
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should fall back to HELO if needed', async () => {
|
|
// Most modern servers support EHLO, but client should handle HELO fallback
|
|
const heloClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
domain: 'legacy.example.com',
|
|
connectionTimeout: 5000,
|
|
debug: true
|
|
});
|
|
|
|
// The client should handle EHLO/HELO automatically
|
|
const isConnected = await heloClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
|
|
await heloClient.close();
|
|
console.log('✅ EHLO/HELO fallback mechanism working');
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should parse server capabilities', async () => {
|
|
const capClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000,
|
|
pool: true, // Enable pooling to maintain connections
|
|
debug: true
|
|
});
|
|
|
|
// verify() creates a temporary connection and closes it
|
|
const verifyResult = await capClient.verify();
|
|
expect(verifyResult).toBeTrue();
|
|
|
|
// After verify(), the pool might be empty since verify() closes its connection
|
|
// Instead, let's send an actual email to test capabilities
|
|
const poolStatus = capClient.getPoolStatus();
|
|
|
|
// Pool starts empty
|
|
expect(poolStatus.total).toEqual(0);
|
|
|
|
await capClient.close();
|
|
console.log('✅ Server capabilities parsed from EHLO response');
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should handle very long domain names', async () => {
|
|
const longDomain = 'very-long-subdomain.with-many-parts.and-labels.example.com';
|
|
|
|
const longDomainClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
domain: longDomain,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const isConnected = await longDomainClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
|
|
await longDomainClient.close();
|
|
console.log('✅ Long domain name handled correctly');
|
|
});
|
|
|
|
tap.test('CCMD-01: EHLO/HELO - should reconnect with EHLO after disconnect', async () => {
|
|
// First connection - verify() creates and closes its own connection
|
|
const firstVerify = await smtpClient.verify();
|
|
expect(firstVerify).toBeTrue();
|
|
|
|
// After verify(), no connections should be in the pool
|
|
expect(smtpClient.isConnected()).toBeFalse();
|
|
|
|
// Second verify - should send EHLO again
|
|
const secondVerify = await smtpClient.verify();
|
|
expect(secondVerify).toBeTrue();
|
|
|
|
console.log('✅ EHLO sent correctly on reconnection');
|
|
});
|
|
|
|
tap.test('cleanup - close SMTP client', async () => {
|
|
if (smtpClient && smtpClient.isConnected()) {
|
|
await smtpClient.close();
|
|
}
|
|
});
|
|
|
|
tap.test('cleanup - stop SMTP server', async () => {
|
|
await stopTestServer(testServer);
|
|
});
|
|
|
|
export default tap.start(); |