BREAKING CHANGE(smtp-client): Replace the legacy TypeScript SMTP client with a new Rust-based SMTP client and IPC bridge for outbound delivery
This commit is contained in:
107
test/test.smtp.client.rust.node.ts
Normal file
107
test/test.smtp.client.rust.node.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { RustSecurityBridge, BridgeState } from '../ts/security/classes.rustsecuritybridge.js';
|
||||
import type { ISmtpSendOptions, ISmtpPoolStatus } from '../ts/security/classes.rustsecuritybridge.js';
|
||||
|
||||
let bridge: RustSecurityBridge;
|
||||
|
||||
tap.test('Rust SMTP Client - should start bridge', async () => {
|
||||
RustSecurityBridge.resetInstance();
|
||||
bridge = RustSecurityBridge.getInstance();
|
||||
const ok = await bridge.start();
|
||||
if (!ok) {
|
||||
console.log('WARNING: Rust binary not available — skipping Rust SMTP client tests');
|
||||
console.log('Build it with: cd rust && cargo build --release');
|
||||
}
|
||||
expect(typeof ok).toEqual('boolean');
|
||||
});
|
||||
|
||||
tap.test('Rust SMTP Client - getSmtpPoolStatus returns valid structure', async () => {
|
||||
if (!bridge.running) {
|
||||
console.log('SKIP: bridge not running');
|
||||
return;
|
||||
}
|
||||
const status: ISmtpPoolStatus = await bridge.getSmtpPoolStatus();
|
||||
expect(status).toBeTruthy();
|
||||
expect(status.pools).toBeTruthy();
|
||||
expect(typeof status.pools).toEqual('object');
|
||||
});
|
||||
|
||||
tap.test('Rust SMTP Client - verifySmtpConnection with unreachable host', async () => {
|
||||
if (!bridge.running) {
|
||||
console.log('SKIP: bridge not running');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Use a non-routable IP to test connection failure handling
|
||||
const result = await bridge.verifySmtpConnection({
|
||||
host: '192.0.2.1', // TEST-NET-1 (RFC 5737) - guaranteed unreachable
|
||||
port: 25,
|
||||
secure: false,
|
||||
domain: 'test.example.com',
|
||||
});
|
||||
// If it returns rather than throwing, reachable should be false
|
||||
expect(result.reachable).toBeFalse();
|
||||
} catch (err) {
|
||||
// Connection errors are expected for unreachable hosts
|
||||
expect(err).toBeTruthy();
|
||||
expect(err.message || String(err)).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('Rust SMTP Client - sendEmail with connection refused error', async () => {
|
||||
if (!bridge.running) {
|
||||
console.log('SKIP: bridge not running');
|
||||
return;
|
||||
}
|
||||
const opts: ISmtpSendOptions = {
|
||||
host: '127.0.0.1',
|
||||
port: 52599, // random high port - should be refused
|
||||
secure: false,
|
||||
domain: 'test.example.com',
|
||||
email: {
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Test email',
|
||||
text: 'This is a test.',
|
||||
},
|
||||
connectionTimeoutSecs: 5,
|
||||
socketTimeoutSecs: 5,
|
||||
};
|
||||
|
||||
try {
|
||||
await bridge.sendOutboundEmail(opts);
|
||||
// Should not succeed — no server is listening
|
||||
throw new Error('Expected sendEmail to fail on connection refused');
|
||||
} catch (err) {
|
||||
// We expect a connection error
|
||||
expect(err).toBeTruthy();
|
||||
const msg = err.message || String(err);
|
||||
expect(msg.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('Rust SMTP Client - closeSmtpPool cleans up', async () => {
|
||||
if (!bridge.running) {
|
||||
console.log('SKIP: bridge not running');
|
||||
return;
|
||||
}
|
||||
// Should not throw, even when no pools exist
|
||||
await bridge.closeSmtpPool();
|
||||
|
||||
// Verify pool status is empty after close
|
||||
const status = await bridge.getSmtpPoolStatus();
|
||||
expect(status.pools).toBeTruthy();
|
||||
const poolKeys = Object.keys(status.pools);
|
||||
expect(poolKeys.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('Rust SMTP Client - stop bridge', async () => {
|
||||
if (!bridge.running) {
|
||||
console.log('SKIP: bridge not running');
|
||||
return;
|
||||
}
|
||||
await bridge.stop();
|
||||
expect(bridge.state).toEqual(BridgeState.Stopped);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
Reference in New Issue
Block a user