- Ported CMD-06 RSET Command tests with 8 passing tests covering transaction resets and recipient clearing. - Ported SEC-06 IP Reputation tests with 7 passing tests validating infrastructure and legitimate traffic acceptance. - Ported ERR-01 Syntax Error tests with 10 passing tests for handling invalid commands and syntax errors. - Updated README files to reflect the new test statuses and coverage. - Added detailed test cases for handling invalid sequences in ERR-02 tests.
226 lines
6.7 KiB
TypeScript
226 lines
6.7 KiB
TypeScript
/**
|
|
* CMD-06: RSET Command Tests
|
|
* Tests SMTP RSET command for transaction reset
|
|
*/
|
|
|
|
import { assert, assertEquals, assertMatch } from '@std/assert';
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.ts';
|
|
import {
|
|
connectToSmtp,
|
|
waitForGreeting,
|
|
sendSmtpCommand,
|
|
closeSmtpConnection,
|
|
} from '../../helpers/utils.ts';
|
|
|
|
const TEST_PORT = 25259;
|
|
let testServer: ITestServer;
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: Setup - Start SMTP server',
|
|
async fn() {
|
|
testServer = await startTestServer({ port: TEST_PORT });
|
|
assert(testServer, 'Test server should be created');
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - resets transaction after MAIL FROM',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
await sendSmtpCommand(conn, 'MAIL FROM:<sender@example.com>', '250');
|
|
|
|
// Send RSET
|
|
const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK');
|
|
|
|
// After RSET, should be able to send new MAIL FROM
|
|
const mailFromResponse = await sendSmtpCommand(conn, 'MAIL FROM:<newsender@example.com>', '250');
|
|
assert(mailFromResponse.includes('250'), 'Should accept MAIL FROM after RSET');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ RSET successfully resets transaction after MAIL FROM');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - resets transaction after RCPT TO',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
await sendSmtpCommand(conn, 'MAIL FROM:<sender@example.com>', '250');
|
|
await sendSmtpCommand(conn, 'RCPT TO:<recipient@example.com>', '250');
|
|
|
|
// Send RSET
|
|
const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK');
|
|
|
|
// After RSET, RCPT TO should fail (need MAIL FROM first)
|
|
const rcptToResponse = await sendSmtpCommand(conn, 'RCPT TO:<newrecipient@example.com>');
|
|
assertMatch(rcptToResponse, /^503/, 'Should reject RCPT TO without MAIL FROM after RSET');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ RSET clears transaction state requiring new MAIL FROM');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - handles multiple consecutive RSET commands',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
await sendSmtpCommand(conn, 'MAIL FROM:<sender@example.com>', '250');
|
|
|
|
// Send multiple RSETs
|
|
const rset1 = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rset1.includes('250'), 'First RSET should succeed');
|
|
|
|
const rset2 = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rset2.includes('250'), 'Second RSET should succeed');
|
|
|
|
const rset3 = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rset3.includes('250'), 'Third RSET should succeed');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ Multiple consecutive RSET commands work (idempotent)');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - works without active transaction',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
|
|
// Send RSET without any transaction
|
|
const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rsetResponse.includes('250'), 'RSET should work even without active transaction');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ RSET works without active transaction');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - clears all recipients',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
await sendSmtpCommand(conn, 'MAIL FROM:<sender@example.com>', '250');
|
|
|
|
// Add multiple recipients
|
|
await sendSmtpCommand(conn, 'RCPT TO:<recipient1@example.com>', '250');
|
|
await sendSmtpCommand(conn, 'RCPT TO:<recipient2@example.com>', '250');
|
|
await sendSmtpCommand(conn, 'RCPT TO:<recipient3@example.com>', '250');
|
|
|
|
// Send RSET
|
|
const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250');
|
|
assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK');
|
|
|
|
// After RSET, DATA should fail (no recipients)
|
|
const dataResponse = await sendSmtpCommand(conn, 'DATA');
|
|
assertMatch(dataResponse, /^503/, 'DATA should fail without recipients after RSET');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ RSET clears all recipients from transaction');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: RSET - ignores parameters',
|
|
async fn() {
|
|
const conn = await connectToSmtp('localhost', TEST_PORT);
|
|
|
|
try {
|
|
await waitForGreeting(conn);
|
|
await sendSmtpCommand(conn, 'EHLO test.example.com', '250');
|
|
|
|
// Send RSET with parameters (should be ignored)
|
|
const rsetResponse = await sendSmtpCommand(conn, 'RSET ignored parameter', '250');
|
|
assert(rsetResponse.includes('250'), 'RSET should ignore parameters and respond with 250 OK');
|
|
|
|
await sendSmtpCommand(conn, 'QUIT', '221');
|
|
console.log('✓ RSET ignores parameters as per RFC');
|
|
} finally {
|
|
try {
|
|
conn.close();
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|
|
|
|
Deno.test({
|
|
name: 'CMD-06: Cleanup - Stop SMTP server',
|
|
async fn() {
|
|
await stopTestServer(testServer);
|
|
},
|
|
sanitizeResources: false,
|
|
sanitizeOps: false,
|
|
});
|