update
This commit is contained in:
296
test/suite/smtpserver_commands/test.cmd-03.rcpt-to.ts
Normal file
296
test/suite/smtpserver_commands/test.cmd-03.rcpt-to.ts
Normal file
@ -0,0 +1,296 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as net from 'net';
|
||||
import { startTestServer, stopTestServer } from '../../helpers/server.loader.js';
|
||||
|
||||
const TEST_PORT = 2525;
|
||||
|
||||
let testServer;
|
||||
const TEST_TIMEOUT = 10000;
|
||||
|
||||
tap.test('prepare server', async () => {
|
||||
testServer = await startTestServer({ port: TEST_PORT });
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
});
|
||||
|
||||
tap.test('RCPT TO - should accept valid recipient after MAIL FROM', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: TEST_TIMEOUT
|
||||
});
|
||||
|
||||
let receivedData = '';
|
||||
let currentStep = 'connecting';
|
||||
|
||||
socket.on('data', (data) => {
|
||||
receivedData += data.toString();
|
||||
|
||||
if (currentStep === 'connecting' && receivedData.includes('220')) {
|
||||
currentStep = 'ehlo';
|
||||
receivedData = '';
|
||||
socket.write('EHLO test.example.com\r\n');
|
||||
} else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
|
||||
currentStep = 'mail_from';
|
||||
receivedData = '';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
} else if (currentStep === 'mail_from' && receivedData.includes('250')) {
|
||||
currentStep = 'rcpt_to';
|
||||
receivedData = '';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
} else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
|
||||
expect(receivedData).toInclude('250');
|
||||
socket.write('QUIT\r\n');
|
||||
setTimeout(() => {
|
||||
socket.destroy();
|
||||
done.resolve();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
done.reject(error);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
done.reject(new Error(`Connection timeout at step: ${currentStep}`));
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('RCPT TO - should reject without MAIL FROM', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: TEST_TIMEOUT
|
||||
});
|
||||
|
||||
let receivedData = '';
|
||||
let currentStep = 'connecting';
|
||||
|
||||
socket.on('data', (data) => {
|
||||
receivedData += data.toString();
|
||||
|
||||
if (currentStep === 'connecting' && receivedData.includes('220')) {
|
||||
currentStep = 'ehlo';
|
||||
receivedData = '';
|
||||
socket.write('EHLO test.example.com\r\n');
|
||||
} else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
|
||||
currentStep = 'rcpt_to_without_mail';
|
||||
receivedData = '';
|
||||
// Try RCPT TO without MAIL FROM
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
} else if (currentStep === 'rcpt_to_without_mail' && receivedData.includes('503')) {
|
||||
// Should get 503 (bad sequence)
|
||||
expect(receivedData).toInclude('503');
|
||||
socket.write('QUIT\r\n');
|
||||
setTimeout(() => {
|
||||
socket.destroy();
|
||||
done.resolve();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
done.reject(error);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
done.reject(new Error(`Connection timeout at step: ${currentStep}`));
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('RCPT TO - should accept multiple recipients', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: TEST_TIMEOUT
|
||||
});
|
||||
|
||||
let receivedData = '';
|
||||
let currentStep = 'connecting';
|
||||
let recipientCount = 0;
|
||||
const maxRecipients = 3;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
receivedData += data.toString();
|
||||
|
||||
if (currentStep === 'connecting' && receivedData.includes('220')) {
|
||||
currentStep = 'ehlo';
|
||||
receivedData = '';
|
||||
socket.write('EHLO test.example.com\r\n');
|
||||
} else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
|
||||
currentStep = 'mail_from';
|
||||
receivedData = '';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
} else if (currentStep === 'mail_from' && receivedData.includes('250')) {
|
||||
currentStep = 'rcpt_to';
|
||||
receivedData = '';
|
||||
socket.write(`RCPT TO:<recipient${recipientCount + 1}@example.com>\r\n`);
|
||||
} else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
|
||||
recipientCount++;
|
||||
receivedData = '';
|
||||
|
||||
if (recipientCount < maxRecipients) {
|
||||
socket.write(`RCPT TO:<recipient${recipientCount + 1}@example.com>\r\n`);
|
||||
} else {
|
||||
expect(recipientCount).toEqual(maxRecipients);
|
||||
socket.write('QUIT\r\n');
|
||||
setTimeout(() => {
|
||||
socket.destroy();
|
||||
done.resolve();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
done.reject(error);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
done.reject(new Error(`Connection timeout at step: ${currentStep}`));
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('RCPT TO - should reject invalid email format', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: TEST_TIMEOUT
|
||||
});
|
||||
|
||||
let receivedData = '';
|
||||
let currentStep = 'connecting';
|
||||
let testIndex = 0;
|
||||
|
||||
const invalidRecipients = [
|
||||
'notanemail',
|
||||
'@example.com',
|
||||
'user@',
|
||||
'user@.com',
|
||||
'user@domain..com'
|
||||
];
|
||||
|
||||
socket.on('data', (data) => {
|
||||
receivedData += data.toString();
|
||||
|
||||
if (currentStep === 'connecting' && receivedData.includes('220')) {
|
||||
currentStep = 'ehlo';
|
||||
receivedData = '';
|
||||
socket.write('EHLO test.example.com\r\n');
|
||||
} else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
|
||||
currentStep = 'mail_from';
|
||||
receivedData = '';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
} else if (currentStep === 'mail_from' && receivedData.includes('250')) {
|
||||
currentStep = 'rcpt_to';
|
||||
receivedData = '';
|
||||
console.log(`Testing invalid recipient: "${invalidRecipients[testIndex]}"`);
|
||||
socket.write(`RCPT TO:<${invalidRecipients[testIndex]}>\r\n`);
|
||||
} else if (currentStep === 'rcpt_to' && (receivedData.includes('501') || receivedData.includes('5'))) {
|
||||
// Should reject with 5xx error
|
||||
console.log(` Response: ${receivedData.trim()}`);
|
||||
|
||||
testIndex++;
|
||||
if (testIndex < invalidRecipients.length) {
|
||||
currentStep = 'rset';
|
||||
receivedData = '';
|
||||
socket.write('RSET\r\n');
|
||||
} else {
|
||||
socket.write('QUIT\r\n');
|
||||
setTimeout(() => {
|
||||
socket.destroy();
|
||||
done.resolve();
|
||||
}, 100);
|
||||
}
|
||||
} else if (currentStep === 'rset' && receivedData.includes('250')) {
|
||||
currentStep = 'mail_from';
|
||||
receivedData = '';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
done.reject(error);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
done.reject(new Error(`Connection timeout at step: ${currentStep}`));
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('RCPT TO - should handle SIZE parameter', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: TEST_TIMEOUT
|
||||
});
|
||||
|
||||
let receivedData = '';
|
||||
let currentStep = 'connecting';
|
||||
|
||||
socket.on('data', (data) => {
|
||||
receivedData += data.toString();
|
||||
|
||||
if (currentStep === 'connecting' && receivedData.includes('220')) {
|
||||
currentStep = 'ehlo';
|
||||
receivedData = '';
|
||||
socket.write('EHLO test.example.com\r\n');
|
||||
} else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
|
||||
currentStep = 'mail_from';
|
||||
receivedData = '';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
} else if (currentStep === 'mail_from' && receivedData.includes('250')) {
|
||||
currentStep = 'rcpt_to_with_size';
|
||||
receivedData = '';
|
||||
// RCPT TO doesn't typically have SIZE parameter, but test server response
|
||||
socket.write('RCPT TO:<recipient@example.com> SIZE=1024\r\n');
|
||||
} else if (currentStep === 'rcpt_to_with_size') {
|
||||
// Server might accept or reject the parameter
|
||||
expect(receivedData).toMatch(/^(250|555|501)/);
|
||||
socket.write('QUIT\r\n');
|
||||
setTimeout(() => {
|
||||
socket.destroy();
|
||||
done.resolve();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
done.reject(error);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
done.reject(new Error(`Connection timeout at step: ${currentStep}`));
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('cleanup server', async () => {
|
||||
await stopTestServer(testServer);
|
||||
});
|
||||
|
||||
tap.start();
|
Reference in New Issue
Block a user