dcrouter/test/suite/smtpclient_commands/test.ccmd-08.rset-command.ts
2025-05-26 04:09:29 +00:00

333 lines
8.9 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';
import { Email } from '../../../ts/mail/core/classes.email.js';
let testServer: ITestServer;
tap.test('setup test SMTP server', async () => {
testServer = await startTestServer({
port: 2548,
tlsEnabled: false,
authRequired: false
});
expect(testServer).toBeTruthy();
expect(testServer.port).toBeGreaterThan(0);
});
tap.test('CCMD-08: Client handles transaction reset internally', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Send first email
const email1 = new Email({
from: 'sender1@example.com',
to: 'recipient1@example.com',
subject: 'First Email',
text: 'This is the first email'
});
const result1 = await smtpClient.sendMail(email1);
expect(result1.success).toBeTrue();
// Send second email - client handles RSET internally if needed
const email2 = new Email({
from: 'sender2@example.com',
to: 'recipient2@example.com',
subject: 'Second Email',
text: 'This is the second email'
});
const result2 = await smtpClient.sendMail(email2);
expect(result2.success).toBeTrue();
console.log('✅ Client handles transaction reset between emails');
console.log('RSET is used internally to ensure clean state');
await smtpClient.close();
});
tap.test('CCMD-08: Clean state after failed recipient', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Send email with multiple recipients - if one fails, RSET ensures clean state
const email = new Email({
from: 'sender@example.com',
to: [
'valid1@example.com',
'valid2@example.com',
'valid3@example.com'
],
subject: 'Multi-recipient Email',
text: 'Testing state management'
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
// All recipients should be accepted
expect(result.acceptedRecipients.length).toEqual(3);
console.log('✅ State remains clean with multiple recipients');
console.log('Internal RSET ensures proper transaction handling');
await smtpClient.close();
});
tap.test('CCMD-08: Multiple emails in sequence', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Send multiple emails in sequence
const emails = [
{
from: 'sender1@example.com',
to: 'recipient1@example.com',
subject: 'Email 1',
text: 'First email'
},
{
from: 'sender2@example.com',
to: 'recipient2@example.com',
subject: 'Email 2',
text: 'Second email'
},
{
from: 'sender3@example.com',
to: 'recipient3@example.com',
subject: 'Email 3',
text: 'Third email'
}
];
for (const emailData of emails) {
const email = new Email(emailData);
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
}
console.log('✅ Successfully sent multiple emails in sequence');
console.log('RSET ensures clean state between each transaction');
await smtpClient.close();
});
tap.test('CCMD-08: Connection pooling with clean state', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 2,
connectionTimeout: 5000,
debug: true
});
// Send emails concurrently
const promises = Array.from({ length: 5 }, (_, i) => {
const email = new Email({
from: `sender${i}@example.com`,
to: `recipient${i}@example.com`,
subject: `Pooled Email ${i}`,
text: `This is pooled email ${i}`
});
return smtpClient.sendMail(email);
});
const results = await Promise.all(promises);
// Check results and log any failures
results.forEach((result, index) => {
console.log(`Email ${index}: ${result.success ? '✅' : '❌'} ${!result.success ? result.error?.message : ''}`);
});
// With connection pooling, at least some emails should succeed
const successCount = results.filter(r => r.success).length;
console.log(`Successfully sent ${successCount} of ${results.length} emails`);
expect(successCount).toBeGreaterThan(0);
console.log('✅ Connection pool maintains clean state');
console.log('RSET ensures each pooled connection starts fresh');
await smtpClient.close();
});
tap.test('CCMD-08: Error recovery with state reset', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// First, try with invalid sender (should fail early)
try {
const badEmail = new Email({
from: '', // Invalid
to: 'recipient@example.com',
subject: 'Bad Email',
text: 'This should fail'
});
await smtpClient.sendMail(badEmail);
} catch (error) {
// Expected to fail
console.log('✅ Invalid email rejected as expected');
}
// Now send a valid email - should work fine
const goodEmail = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Good Email',
text: 'This should succeed'
});
const result = await smtpClient.sendMail(goodEmail);
expect(result.success).toBeTrue();
console.log('✅ State recovered after error');
console.log('RSET ensures clean state after failures');
await smtpClient.close();
});
tap.test('CCMD-08: Verify command maintains session', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// verify() creates temporary connection
const verified1 = await smtpClient.verify();
expect(verified1).toBeTrue();
// Send email after verify
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'After Verify',
text: 'Email after verification'
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
// verify() again
const verified2 = await smtpClient.verify();
expect(verified2).toBeTrue();
console.log('✅ Verify operations maintain clean session state');
console.log('Each operation ensures proper state management');
await smtpClient.close();
});
tap.test('CCMD-08: Rapid sequential sends', async () => {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Send emails rapidly
const count = 10;
const startTime = Date.now();
for (let i = 0; i < count; i++) {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: `Rapid Email ${i}`,
text: `Rapid test email ${i}`
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
}
const elapsed = Date.now() - startTime;
const avgTime = elapsed / count;
console.log(`✅ Sent ${count} emails in ${elapsed}ms`);
console.log(`Average time per email: ${avgTime.toFixed(2)}ms`);
console.log('RSET maintains efficiency in rapid sends');
await smtpClient.close();
});
tap.test('CCMD-08: State isolation between clients', async () => {
// Create two separate clients
const client1 = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000
});
const client2 = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000
});
// Send from both clients
const email1 = new Email({
from: 'client1@example.com',
to: 'recipient1@example.com',
subject: 'From Client 1',
text: 'Email from client 1'
});
const email2 = new Email({
from: 'client2@example.com',
to: 'recipient2@example.com',
subject: 'From Client 2',
text: 'Email from client 2'
});
// Send concurrently
const [result1, result2] = await Promise.all([
client1.sendMail(email1),
client2.sendMail(email2)
]);
expect(result1.success).toBeTrue();
expect(result2.success).toBeTrue();
console.log('✅ Each client maintains isolated state');
console.log('RSET ensures no cross-contamination');
await client1.close();
await client2.close();
});
tap.test('cleanup test SMTP server', async () => {
await stopTestServer(testServer);
expect(testServer).toBeTruthy();
});
export default tap.start();