update
This commit is contained in:
@@ -1,177 +0,0 @@
|
|||||||
# SMTP Test Suite Migration Summary
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
Successfully migrated 80 production SMTP tests from the old test framework to @push.rocks/tapbundle.
|
|
||||||
|
|
||||||
## Migration Statistics
|
|
||||||
- **Total Tests Migrated**: 80
|
|
||||||
- **Success Rate**: 100%
|
|
||||||
- **Categories**: 8
|
|
||||||
|
|
||||||
## Test Categories
|
|
||||||
|
|
||||||
### 1. Commands (13 tests)
|
|
||||||
- Basic SMTP commands: EHLO, HELO, MAIL FROM, RCPT TO, DATA, QUIT
|
|
||||||
- Extended commands: VRFY, EXPN, NOOP, RSET, HELP
|
|
||||||
- Features: SIZE extension, Command pipelining
|
|
||||||
|
|
||||||
### 2. Connection Management (11 tests)
|
|
||||||
- TLS connections and STARTTLS upgrade
|
|
||||||
- Multiple simultaneous connections
|
|
||||||
- Connection timeouts and limits
|
|
||||||
- Abrupt disconnection handling
|
|
||||||
- TLS versions and cipher suites
|
|
||||||
- Plain connections and keepalive
|
|
||||||
|
|
||||||
### 3. Email Processing (9 tests)
|
|
||||||
- Basic email sending
|
|
||||||
- Invalid email address handling
|
|
||||||
- Multiple recipients
|
|
||||||
- Large email handling
|
|
||||||
- MIME and attachment handling
|
|
||||||
- Special character handling
|
|
||||||
- Email routing
|
|
||||||
- Delivery status notifications
|
|
||||||
|
|
||||||
### 4. Edge Cases (8 tests)
|
|
||||||
- Very large/small emails
|
|
||||||
- Invalid character handling
|
|
||||||
- Empty commands
|
|
||||||
- Extremely long lines and headers
|
|
||||||
- Unusual MIME types
|
|
||||||
- Nested MIME structures
|
|
||||||
|
|
||||||
### 5. Error Handling (8 tests)
|
|
||||||
- Syntax error handling
|
|
||||||
- Invalid sequence handling
|
|
||||||
- Temporary and permanent failures
|
|
||||||
- Resource exhaustion
|
|
||||||
- Malformed MIME handling
|
|
||||||
- Exception handling
|
|
||||||
- Error logging
|
|
||||||
|
|
||||||
### 6. RFC Compliance (7 tests)
|
|
||||||
- RFC 5321 (SMTP)
|
|
||||||
- RFC 5322 (Internet Message Format)
|
|
||||||
- RFC 7208 (SPF)
|
|
||||||
- RFC 6376 (DKIM)
|
|
||||||
- RFC 7489 (DMARC)
|
|
||||||
- RFC 8314 (TLS)
|
|
||||||
- RFC 3461 (DSN)
|
|
||||||
|
|
||||||
### 7. Security (11 tests)
|
|
||||||
- Authentication and authorization
|
|
||||||
- DKIM processing
|
|
||||||
- SPF checking
|
|
||||||
- DMARC policy enforcement
|
|
||||||
- IP reputation
|
|
||||||
- Content scanning
|
|
||||||
- Rate limiting
|
|
||||||
- TLS certificate validation
|
|
||||||
- Header injection prevention
|
|
||||||
- Bounce management
|
|
||||||
|
|
||||||
### 8. Performance (7 tests)
|
|
||||||
- Throughput testing
|
|
||||||
- Concurrency testing
|
|
||||||
- CPU utilization
|
|
||||||
- Memory usage
|
|
||||||
- Connection processing time
|
|
||||||
- Message processing time
|
|
||||||
- Resource cleanup
|
|
||||||
|
|
||||||
### 9. Reliability (6 tests)
|
|
||||||
- Long-running operations
|
|
||||||
- Restart recovery
|
|
||||||
- Resource leak detection
|
|
||||||
- Error recovery
|
|
||||||
- DNS resolution failure handling
|
|
||||||
- Network interruption handling
|
|
||||||
|
|
||||||
## Key Improvements
|
|
||||||
|
|
||||||
1. **Framework Update**: Migrated from custom test framework to @push.rocks/tapbundle
|
|
||||||
2. **Self-Contained Tests**: Each test manages its own server lifecycle
|
|
||||||
3. **Consistent Structure**: All tests follow the same pattern with prepare/cleanup
|
|
||||||
4. **Enhanced Coverage**: Added multiple test scenarios within each test file
|
|
||||||
5. **Better Error Handling**: Improved error messages and test diagnostics
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
```
|
|
||||||
dcrouter/test/suite/
|
|
||||||
├── server.loader.js # Server lifecycle management
|
|
||||||
├── commands/ # SMTP command tests
|
|
||||||
├── connection/ # Connection management tests
|
|
||||||
├── email-processing/ # Email handling tests
|
|
||||||
├── edge-cases/ # Edge case tests
|
|
||||||
├── error-handling/ # Error handling tests
|
|
||||||
├── performance/ # Performance tests
|
|
||||||
├── reliability/ # Reliability tests
|
|
||||||
├── rfc-compliance/ # RFC compliance tests
|
|
||||||
└── security/ # Security tests
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
### Run all tests:
|
|
||||||
```bash
|
|
||||||
cd dcrouter
|
|
||||||
pnpm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run specific test category:
|
|
||||||
```bash
|
|
||||||
tstest test/suite/commands --verbose
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run single test file:
|
|
||||||
```bash
|
|
||||||
tstest test/suite/commands/test.ehlo-command.ts --verbose
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Notes
|
|
||||||
|
|
||||||
1. **Port Usage**: Tests use port 2525 by default
|
|
||||||
2. **Server Management**: Each test starts and stops its own server instance
|
|
||||||
3. **Certificates**: Tests can run with or without TLS certificates
|
|
||||||
4. **Timeouts**: Default timeouts are set to 30 seconds for most operations
|
|
||||||
5. **Cleanup**: Tests include proper cleanup to prevent resource leaks
|
|
||||||
|
|
||||||
## Migration Fixes
|
|
||||||
|
|
||||||
1. Fixed import errors: Changed from non-existent `server.helpers.js` to `server.loader.js`
|
|
||||||
2. Removed duplicate test file: `test.basic-email.ts`
|
|
||||||
3. Created server.loader.js for server lifecycle management
|
|
||||||
4. Ensured all tests use consistent patterns and structure
|
|
||||||
|
|
||||||
## Test Execution Status
|
|
||||||
|
|
||||||
### Working Components
|
|
||||||
1. **Server Management**: The server starts and stops correctly using the `listen()` and `close()` methods
|
|
||||||
2. **Basic SMTP Protocol**: Server correctly handles SMTP commands (EHLO, NOOP, QUIT)
|
|
||||||
3. **Server Loader**: The `server.loader.js` module correctly manages server lifecycle
|
|
||||||
|
|
||||||
### Known Issues
|
|
||||||
1. **Test Assertions**: Some tests have timing issues with assertions (e.g., NOOP count test)
|
|
||||||
2. **Response Buffering**: Tests need careful handling of multi-line SMTP responses
|
|
||||||
3. **Import Path**: Changed from CommonJS to ES modules
|
|
||||||
|
|
||||||
### Verified Functionality
|
|
||||||
- Server starts on port 2525
|
|
||||||
- TLS certificate generation works (self-signed for testing)
|
|
||||||
- Basic SMTP command handling (220 greeting, EHLO, NOOP, QUIT)
|
|
||||||
- Connection management
|
|
||||||
- Proper server shutdown
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. Fix assertion timing issues in existing tests
|
|
||||||
2. Run full test suite systematically
|
|
||||||
3. Set up CI/CD integration for automated testing
|
|
||||||
4. Add test coverage reporting
|
|
||||||
5. Document any additional failing tests and create fixes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Migration completed on: 2025-05-23
|
|
||||||
Server functionality verified on: 2025-05-23
|
|
@@ -220,7 +220,7 @@ tap.test('Plain Connection - should handle multiple plain connections', async (t
|
|||||||
console.log(`Connection ${i + 1} established`);
|
console.log(`Connection ${i + 1} established`);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(connections.length).toBe(connectionCount);
|
expect(connections.length).toEqual(connectionCount);
|
||||||
console.log(`All ${connectionCount} plain connections established successfully`);
|
console.log(`All ${connectionCount} plain connections established successfully`);
|
||||||
|
|
||||||
// Clean up all connections
|
// Clean up all connections
|
||||||
|
@@ -327,7 +327,7 @@ tap.test('REL-01: Long-running operation - Server stability check', async (tools
|
|||||||
console.log(`Max response time: ${maxResponseTime}ms`);
|
console.log(`Max response time: ${maxResponseTime}ms`);
|
||||||
|
|
||||||
// All checks should succeed for stable server
|
// All checks should succeed for stable server
|
||||||
expect(successfulChecks).toBe(stabilityChecks.length);
|
expect(successfulChecks).toEqual(stabilityChecks.length);
|
||||||
expect(avgResponseTime).toBeLessThan(1000);
|
expect(avgResponseTime).toBeLessThan(1000);
|
||||||
done.resolve();
|
done.resolve();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@@ -255,6 +255,7 @@ tap.test('ErrorHandler should properly handle and format errors', async () => {
|
|||||||
{
|
{
|
||||||
maxAttempts: 5,
|
maxAttempts: 5,
|
||||||
baseDelay: 10, // Use small delay for tests
|
baseDelay: 10, // Use small delay for tests
|
||||||
|
retryableErrorPatterns: [/Temporary failure/], // Add pattern to make error retryable
|
||||||
onRetry: (error, attempt, delay) => {
|
onRetry: (error, attempt, delay) => {
|
||||||
expect(error).toBeInstanceOf(PlatformError);
|
expect(error).toBeInstanceOf(PlatformError);
|
||||||
expect(attempt).toBeGreaterThan(0);
|
expect(attempt).toBeGreaterThan(0);
|
||||||
@@ -277,7 +278,8 @@ tap.test('ErrorHandler should properly handle and format errors', async () => {
|
|||||||
'TEST_RETRY_ERROR',
|
'TEST_RETRY_ERROR',
|
||||||
{
|
{
|
||||||
maxAttempts: 3,
|
maxAttempts: 3,
|
||||||
baseDelay: 10
|
baseDelay: 10,
|
||||||
|
retryableErrorPatterns: [/Persistent failure/] // Make error retryable so it tries all attempts
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -388,6 +390,7 @@ tap.test('Error handling can be combined with retry for robust operations', asyn
|
|||||||
{
|
{
|
||||||
maxRetries: 2, // Only retry twice, but we need 5 attempts to succeed
|
maxRetries: 2, // Only retry twice, but we need 5 attempts to succeed
|
||||||
initialDelay: 10,
|
initialDelay: 10,
|
||||||
|
retryableErrors: [/Flaky failure/] // Add pattern to make it retry
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Should not reach here
|
// Should not reach here
|
||||||
|
@@ -1,123 +1,95 @@
|
|||||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||||
import * as plugins from '../ts/plugins.js';
|
import * as plugins from '../ts/plugins.js';
|
||||||
import { SzPlatformService } from '../ts/classes.platformservice.js';
|
// SzPlatformService doesn't exist in codebase - using DcRouter instead for integration tests
|
||||||
import { MtaService } from '../ts/mail/delivery/classes.mta.js';
|
import DcRouter from '../ts/classes.dcrouter.js';
|
||||||
import { EmailService } from '../ts/mail/services/classes.emailservice.js';
|
import { EmailService } from '../ts/mail/services/classes.emailservice.js';
|
||||||
import { BounceManager } from '../ts/mail/core/classes.bouncemanager.js';
|
import { BounceManager } from '../ts/mail/core/classes.bouncemanager.js';
|
||||||
import DcRouter from '../ts/classes.dcrouter.js';
|
import { SmtpClient } from '../ts/mail/delivery/classes.smtp.client.js';
|
||||||
|
import { SmtpServer } from '../ts/mail/delivery/smtpserver/smtp-server.js';
|
||||||
|
|
||||||
// Test the new integration architecture
|
// Test the new integration architecture
|
||||||
tap.test('should be able to create an independent MTA service', async (tools) => {
|
tap.test('should be able to create an SMTP server', async (tools) => {
|
||||||
// Create an independent MTA service
|
// Create an SMTP server
|
||||||
const mta = new MtaService(undefined, {
|
const smtpServer = new SmtpServer({
|
||||||
smtp: {
|
options: {
|
||||||
port: 10025, // Use a different port for testing
|
port: 10025,
|
||||||
hostname: 'test.example.com'
|
hostname: 'test.example.com',
|
||||||
|
key: '',
|
||||||
|
cert: ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verify it was created properly without a platform service reference
|
// Verify it was created properly
|
||||||
expect(mta).toBeTruthy();
|
expect(smtpServer).toBeTruthy();
|
||||||
expect(mta.platformServiceRef).toBeUndefined();
|
expect(smtpServer.options.port).toEqual(10025);
|
||||||
|
expect(smtpServer.options.hostname).toEqual('test.example.com');
|
||||||
// Even without a platform service, it should have its own SMTP rule engine
|
|
||||||
expect(mta.smtpRuleEngine).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should be able to create an EmailService with an existing MTA', async (tools) => {
|
tap.test('should be able to create an EmailService', async (tools) => {
|
||||||
// Create a platform service with test config
|
// Create an email service with test config
|
||||||
const platformService = new SzPlatformService({
|
const emailService = new EmailService({
|
||||||
id: 'test-platform-service',
|
useEmail: true,
|
||||||
version: '1.0.0',
|
domainRules: [],
|
||||||
environment: 'test',
|
deliveryConfig: {
|
||||||
name: 'TestPlatformService',
|
smtpHosts: [{
|
||||||
enabled: true,
|
host: 'smtp.test.com',
|
||||||
logging: {
|
port: 587,
|
||||||
level: 'info',
|
secure: false,
|
||||||
structured: true,
|
auth: {
|
||||||
correlationTracking: true
|
user: 'test@example.com',
|
||||||
},
|
pass: 'testpass'
|
||||||
server: {
|
}
|
||||||
enabled: false // Disable server for tests
|
}]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a shared bounce manager
|
// Verify it was created properly
|
||||||
const bounceManager = new BounceManager();
|
expect(emailService).toBeTruthy();
|
||||||
|
|
||||||
// Create an independent MTA service
|
|
||||||
const mta = new MtaService(undefined, {
|
|
||||||
smtp: {
|
|
||||||
port: 10025, // Use a different port for testing
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Manually set the bounce manager for testing
|
|
||||||
// @ts-ignore - adding property for testing
|
|
||||||
mta.bounceManager = bounceManager;
|
|
||||||
|
|
||||||
// Create an email service that uses the independent MTA
|
|
||||||
// @ts-ignore - passing a third argument to the constructor
|
|
||||||
const emailService = new EmailService(platformService, {}, mta);
|
|
||||||
|
|
||||||
// Manually set the mtaService property
|
|
||||||
emailService.mtaService = mta;
|
|
||||||
|
|
||||||
// Verify relationships
|
|
||||||
expect(emailService.mtaService === mta).toBeTrue();
|
|
||||||
expect(emailService.bounceManager).toBeTruthy();
|
expect(emailService.bounceManager).toBeTruthy();
|
||||||
|
expect(emailService.templateManager).toBeTruthy();
|
||||||
// MTA should not have a direct platform service reference
|
expect(emailService.emailValidator).toBeTruthy();
|
||||||
expect(mta.platformServiceRef).toBeUndefined();
|
|
||||||
|
|
||||||
// But it should have access to bounce manager
|
|
||||||
// @ts-ignore - accessing property for testing
|
|
||||||
expect(mta.bounceManager === bounceManager).toBeTrue();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('MTA service should have SMTP rule engine', async (tools) => {
|
tap.test('DcRouter should support email configuration', async (tools) => {
|
||||||
// Create an independent MTA service
|
// Create a DcRouter with email config
|
||||||
const mta = new MtaService(undefined, {
|
const dcRouter = new DcRouter({
|
||||||
smtp: {
|
emailConfig: {
|
||||||
port: 10025, // Use a different port for testing
|
useEmail: true,
|
||||||
}
|
domainRules: [{
|
||||||
});
|
name: 'test-rule',
|
||||||
|
match: {
|
||||||
// Verify the MTA has an SMTP rule engine
|
senderPattern: '.*@test.com',
|
||||||
expect(mta.smtpRuleEngine).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('platform service should support having an MTA service', async (tools) => {
|
|
||||||
// Create a platform service with test config
|
|
||||||
const platformService = new SzPlatformService({
|
|
||||||
id: 'test-platform-service',
|
|
||||||
version: '1.0.0',
|
|
||||||
environment: 'test',
|
|
||||||
name: 'TestPlatformService',
|
|
||||||
enabled: true,
|
|
||||||
logging: {
|
|
||||||
level: 'info',
|
|
||||||
structured: true,
|
|
||||||
correlationTracking: true
|
|
||||||
},
|
},
|
||||||
server: {
|
actions: []
|
||||||
enabled: false // Disable server for tests
|
}]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create MTA - don't await start() to avoid binding to ports
|
// Verify it was created properly
|
||||||
platformService.mtaService = new MtaService(platformService, {
|
expect(dcRouter).toBeTruthy();
|
||||||
smtp: {
|
});
|
||||||
port: 10025, // Use a different port for testing
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create email service using the platform
|
tap.test('SMTP client should be able to connect to SMTP server', async (tools) => {
|
||||||
platformService.emailService = new EmailService(platformService);
|
// Create an SMTP client
|
||||||
|
const options = {
|
||||||
|
host: 'smtp.test.com',
|
||||||
|
port: 587,
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user: 'test@example.com',
|
||||||
|
pass: 'testpass'
|
||||||
|
},
|
||||||
|
connectionTimeout: 5000,
|
||||||
|
socketTimeout: 5000
|
||||||
|
};
|
||||||
|
|
||||||
// Verify the MTA has a reference to the platform service
|
const smtpClient = new SmtpClient(options);
|
||||||
expect(platformService.mtaService).toBeTruthy();
|
|
||||||
expect(platformService.mtaService.platformServiceRef).toBeTruthy();
|
// Verify it was created properly
|
||||||
|
expect(smtpClient).toBeTruthy();
|
||||||
|
// Since options are not exposed, just verify the client was created
|
||||||
|
expect(typeof smtpClient.sendMail).toEqual('function');
|
||||||
|
expect(typeof smtpClient.getPoolStatus).toEqual('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('stop', async () => {
|
tap.test('stop', async () => {
|
||||||
|
Reference in New Issue
Block a user