137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
|
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||
|
|
import { RustSecurityBridge } from '../ts/security/classes.rustsecuritybridge.js';
|
||
|
|
|
||
|
|
let bridge: RustSecurityBridge;
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - should get singleton instance', async () => {
|
||
|
|
bridge = RustSecurityBridge.getInstance();
|
||
|
|
expect(bridge).toBeTruthy();
|
||
|
|
expect(bridge).toEqual(RustSecurityBridge.getInstance()); // same instance
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - should start the Rust binary', async () => {
|
||
|
|
const ok = await bridge.start();
|
||
|
|
if (!ok) {
|
||
|
|
console.log('WARNING: Rust binary not available — skipping bridge tests');
|
||
|
|
console.log('Build it with: cd rust && cargo build --release');
|
||
|
|
}
|
||
|
|
// We accept both true and false — the binary may not be built yet
|
||
|
|
expect(typeof ok).toEqual('boolean');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - ping should return pong', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const pong = await bridge.ping();
|
||
|
|
expect(pong).toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - version should return crate versions', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const version = await bridge.getVersion();
|
||
|
|
expect(version.bin).toBeTruthy();
|
||
|
|
expect(version.core).toBeTruthy();
|
||
|
|
expect(version.security).toBeTruthy();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - validateEmail with valid address', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const result = await bridge.validateEmail('test@example.com');
|
||
|
|
expect(result.valid).toBeTrue();
|
||
|
|
expect(result.formatValid).toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - validateEmail with invalid address', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const result = await bridge.validateEmail('not-an-email');
|
||
|
|
expect(result.formatValid).toBeFalse();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - detectBounce with known SMTP response', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const result = await bridge.detectBounce({
|
||
|
|
smtpResponse: '550 5.1.1 User unknown',
|
||
|
|
});
|
||
|
|
expect(result.bounce_type).toBeTruthy();
|
||
|
|
expect(result.category).toBeTruthy();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - checkIpReputation', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Use a well-known IP that should NOT be on blacklists
|
||
|
|
const result = await bridge.checkIpReputation('8.8.8.8');
|
||
|
|
expect(result.ip).toEqual('8.8.8.8');
|
||
|
|
expect(typeof result.score).toEqual('number');
|
||
|
|
expect(result.score).toBeGreaterThan(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - verifyDkim with unsigned message', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const rawMessage = 'From: test@example.com\r\nTo: receiver@example.com\r\nSubject: Test\r\n\r\nHello';
|
||
|
|
const results = await bridge.verifyDkim(rawMessage);
|
||
|
|
expect(results.length).toBeGreaterThan(0);
|
||
|
|
expect(results[0].status).toEqual('none'); // no DKIM signature
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - verifyEmail compound call', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const rawMessage = 'From: test@example.com\r\nTo: receiver@example.com\r\nSubject: Test\r\n\r\nHello';
|
||
|
|
const result = await bridge.verifyEmail({
|
||
|
|
rawMessage,
|
||
|
|
ip: '93.184.216.34', // example.com IP
|
||
|
|
heloDomain: 'example.com',
|
||
|
|
hostname: 'mail.test.local',
|
||
|
|
mailFrom: 'test@example.com',
|
||
|
|
});
|
||
|
|
expect(result.dkim).toBeTruthy();
|
||
|
|
expect(result.dkim.length).toBeGreaterThan(0);
|
||
|
|
expect(result.spf).toBeTruthy();
|
||
|
|
// DMARC may or may not be present depending on DNS
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - should stop gracefully', async () => {
|
||
|
|
if (!bridge.running) {
|
||
|
|
console.log('SKIP: bridge not running');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
await bridge.stop();
|
||
|
|
expect(bridge.running).toBeFalse();
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('RustSecurityBridge - commands should fail when bridge is stopped', async () => {
|
||
|
|
// Bridge should not be running now
|
||
|
|
expect(bridge.running).toBeFalse();
|
||
|
|
try {
|
||
|
|
await bridge.ping();
|
||
|
|
// If we get here, the bridge auto-restarted or something unexpected
|
||
|
|
expect(true).toBeFalse(); // Should not reach here
|
||
|
|
} catch (err) {
|
||
|
|
expect(err).toBeTruthy(); // Expected: bridge not running error
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
export default tap.start();
|