108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
|
|
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();
|