223 lines
No EOL
6.4 KiB
TypeScript
223 lines
No EOL
6.4 KiB
TypeScript
import nodemailer from 'nodemailer';
|
|
import { SMTPWSTransport, createTransport } from '../src/transport';
|
|
|
|
describe('SMTPWSTransport', () => {
|
|
let transport: SMTPWSTransport;
|
|
|
|
beforeEach(() => {
|
|
transport = createTransport({
|
|
host: 'localhost',
|
|
port: 3000,
|
|
auth: {
|
|
user: 'test-api-key'
|
|
},
|
|
debug: false
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await transport.close();
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should create transport with correct configuration', () => {
|
|
expect(transport.name).toBe('SMTPWS');
|
|
expect(transport.version).toBe('1.0.0');
|
|
});
|
|
|
|
it('should handle secure connection configuration', () => {
|
|
const secureTransport = createTransport({
|
|
host: 'localhost',
|
|
port: 443,
|
|
secure: true,
|
|
auth: {
|
|
user: 'test-key'
|
|
},
|
|
debug: false
|
|
});
|
|
|
|
const info = secureTransport.getTransportInfo();
|
|
expect(info.secure).toBe(true);
|
|
expect(info.port).toBe(443);
|
|
});
|
|
});
|
|
|
|
describe('getTransportInfo', () => {
|
|
it('should return transport information', () => {
|
|
const info = transport.getTransportInfo();
|
|
|
|
expect(info).toMatchObject({
|
|
name: 'SMTPWS',
|
|
version: '1.0.0',
|
|
host: 'localhost',
|
|
port: 3000,
|
|
secure: false
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('send', () => {
|
|
it('should send mail message', async () => {
|
|
const mockMail = {
|
|
data: {
|
|
envelope: {
|
|
from: 'test@example.com',
|
|
to: ['recipient@example.com']
|
|
},
|
|
raw: 'Subject: Test\r\n\r\nTest message'
|
|
}
|
|
};
|
|
|
|
// Mock the internal client
|
|
const mockSendCommand = jest.fn()
|
|
.mockResolvedValueOnce('250 Hello') // EHLO
|
|
.mockResolvedValueOnce('250 OK') // MAIL FROM
|
|
.mockResolvedValueOnce('250 OK') // RCPT TO
|
|
.mockResolvedValueOnce('354 Start mail input') // DATA
|
|
.mockResolvedValueOnce('250 Message accepted') // Message content
|
|
.mockResolvedValueOnce('221 Bye'); // QUIT
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
const result = await transport.send(mockMail);
|
|
|
|
expect(result).toMatchObject({
|
|
envelope: mockMail.data.envelope,
|
|
accepted: ['recipient@example.com'],
|
|
rejected: [],
|
|
pending: []
|
|
});
|
|
|
|
expect(mockSendCommand).toHaveBeenCalledTimes(6);
|
|
});
|
|
|
|
it('should handle rejected recipients', async () => {
|
|
const mockMail = {
|
|
data: {
|
|
envelope: {
|
|
from: 'test@example.com',
|
|
to: ['good@example.com', 'bad@example.com']
|
|
},
|
|
raw: 'Subject: Test\r\n\r\nTest message'
|
|
}
|
|
};
|
|
|
|
const mockSendCommand = jest.fn()
|
|
.mockResolvedValueOnce('250 Hello') // EHLO
|
|
.mockResolvedValueOnce('250 OK') // MAIL FROM
|
|
.mockResolvedValueOnce('250 OK') // RCPT TO (good)
|
|
.mockResolvedValueOnce('550 No such user') // RCPT TO (bad)
|
|
.mockResolvedValueOnce('354 Start mail input') // DATA
|
|
.mockResolvedValueOnce('250 Message accepted') // Message content
|
|
.mockResolvedValueOnce('221 Bye'); // QUIT
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
const result = await transport.send(mockMail);
|
|
|
|
expect(result.accepted).toEqual(['good@example.com']);
|
|
expect(result.rejected).toEqual(['bad@example.com']);
|
|
});
|
|
|
|
it('should call callback on success', (done) => {
|
|
const mockMail = {
|
|
data: {
|
|
envelope: {
|
|
from: 'test@example.com',
|
|
to: ['recipient@example.com']
|
|
},
|
|
raw: 'Test message'
|
|
}
|
|
};
|
|
|
|
const mockSendCommand = jest.fn()
|
|
.mockResolvedValueOnce('250 Hello')
|
|
.mockResolvedValueOnce('250 OK')
|
|
.mockResolvedValueOnce('250 OK')
|
|
.mockResolvedValueOnce('354 Start mail input')
|
|
.mockResolvedValueOnce('250 Message accepted')
|
|
.mockResolvedValueOnce('221 Bye');
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
transport.send(mockMail, (err, info) => {
|
|
expect(err).toBeNull();
|
|
expect(info).toBeDefined();
|
|
expect(info?.accepted).toEqual(['recipient@example.com']);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should call callback on error', (done) => {
|
|
const mockMail = {
|
|
envelope: {
|
|
from: 'test@example.com',
|
|
to: ['recipient@example.com']
|
|
},
|
|
raw: 'Test message'
|
|
};
|
|
|
|
const mockSendCommand = jest.fn()
|
|
.mockRejectedValueOnce(new Error('Connection failed'));
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
transport.send(mockMail, (err, info) => {
|
|
expect(err).toBeDefined();
|
|
expect(err?.message).toBe('Connection failed');
|
|
expect(info).toBeUndefined();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('verify', () => {
|
|
it('should verify transport connectivity', async () => {
|
|
const mockSendCommand = jest.fn()
|
|
.mockResolvedValueOnce('250 Hello');
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
const result = await transport.verify();
|
|
expect(result).toBe(true);
|
|
expect(mockSendCommand).toHaveBeenCalledWith('EHLO transport-verify\r\n');
|
|
});
|
|
|
|
it('should throw error on verification failure', async () => {
|
|
const mockSendCommand = jest.fn()
|
|
.mockRejectedValueOnce(new Error('Connection refused'));
|
|
|
|
(transport as any).client.sendSMTPCommand = mockSendCommand;
|
|
|
|
await expect(transport.verify()).rejects.toThrow('Transport verification failed');
|
|
});
|
|
|
|
it('should throw error on missing API key during construction', async () => {
|
|
expect(() => createTransport({
|
|
host: 'localhost',
|
|
auth: { user: '' },
|
|
debug: false
|
|
})).toThrow('API key is required');
|
|
});
|
|
});
|
|
|
|
describe('isIdle', () => {
|
|
it('should return true when transport is idle', () => {
|
|
expect(transport.isIdle()).toBe(true);
|
|
});
|
|
|
|
it('should return false when transport has queued messages', () => {
|
|
// Mock queue size
|
|
(transport as any).client.getQueueSize = jest.fn().mockReturnValue(5);
|
|
|
|
expect(transport.isIdle()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('nodemailer integration', () => {
|
|
it('should work as nodemailer transport', async () => {
|
|
const transporter = nodemailer.createTransport(transport);
|
|
expect(transporter).toBeDefined();
|
|
});
|
|
});
|
|
}); |