feat(wire): Add wire protocol, WireTarget & WireParser, Smartmail JSON serialization; refactor plugins and update dependencies
This commit is contained in:
281
test/test.ts
281
test/test.ts
@@ -1,4 +1,4 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as smartmail from '../ts/index.js';
|
||||
import * as plugins from '../ts/smartmail.plugins.js';
|
||||
|
||||
@@ -134,14 +134,285 @@ tap.test('should add email headers', async () => {
|
||||
body: 'Test body',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
|
||||
headerMailer.addHeader('X-Test-Header', 'TestValue');
|
||||
headerMailer.addHeader('X-Tracking-ID', '12345');
|
||||
|
||||
|
||||
const mimeObj = await headerMailer.toMimeFormat();
|
||||
|
||||
|
||||
expect(mimeObj.headers['X-Test-Header']).toEqual('TestValue');
|
||||
expect(mimeObj.headers['X-Tracking-ID']).toEqual('12345');
|
||||
});
|
||||
|
||||
tap.start();
|
||||
// Fluent Chaining Tests
|
||||
tap.test('should support fluent chaining for all mutation methods', async () => {
|
||||
const email = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Fluent {{name}}',
|
||||
body: 'Hello {{name}}'
|
||||
});
|
||||
|
||||
// Chain all methods together
|
||||
const result = email
|
||||
.addRecipient('user1@example.com')
|
||||
.addRecipient('cc@example.com', 'cc')
|
||||
.addRecipients(['user2@example.com', 'user3@example.com'])
|
||||
.setReplyTo('reply@example.com')
|
||||
.setPriority('high')
|
||||
.addHeader('X-Custom', 'value')
|
||||
.applyVariables({ name: 'John' });
|
||||
|
||||
// Result should be the same instance
|
||||
expect(result).toEqual(email);
|
||||
|
||||
// All values should be set
|
||||
expect(email.options.to!.length).toEqual(3);
|
||||
expect(email.options.cc!.length).toEqual(1);
|
||||
expect(email.options.replyTo).toEqual('reply@example.com');
|
||||
expect(email.options.priority).toEqual('high');
|
||||
expect(email.options.headers!['X-Custom']).toEqual('value');
|
||||
expect(email.options.subject).toEqual('Fluent John');
|
||||
expect(email.options.body).toEqual('Hello John');
|
||||
});
|
||||
|
||||
// Wire Format Serialization Tests
|
||||
tap.test('should serialize to JSON and back with toObject/fromObject', async () => {
|
||||
const original = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
cc: ['cc@example.com'],
|
||||
subject: 'Test Subject',
|
||||
body: 'Test Body',
|
||||
htmlBody: '<p>Test HTML</p>',
|
||||
priority: 'high',
|
||||
headers: { 'X-Custom': 'value' },
|
||||
creationObjectRef: { orderId: '12345' }
|
||||
});
|
||||
|
||||
const obj = original.toObject();
|
||||
|
||||
expect(obj.from).toEqual('sender@example.com');
|
||||
expect(obj.to).toInclude('recipient@example.com');
|
||||
expect(obj.cc).toInclude('cc@example.com');
|
||||
expect(obj.subject).toEqual('Test Subject');
|
||||
expect(obj.body).toEqual('Test Body');
|
||||
expect(obj.htmlBody).toEqual('<p>Test HTML</p>');
|
||||
expect(obj.priority).toEqual('high');
|
||||
expect(obj.headers!['X-Custom']).toEqual('value');
|
||||
expect(obj.creationObjectRef).toEqual({ orderId: '12345' });
|
||||
expect(obj.attachments).toBeDefined();
|
||||
|
||||
// Reconstruct from object
|
||||
const reconstructed = smartmail.Smartmail.fromObject(obj);
|
||||
|
||||
expect(reconstructed.options.from).toEqual(original.options.from);
|
||||
expect(reconstructed.options.subject).toEqual(original.options.subject);
|
||||
expect(reconstructed.options.body).toEqual(original.options.body);
|
||||
expect(reconstructed.options.htmlBody).toEqual(original.options.htmlBody);
|
||||
expect(reconstructed.options.priority).toEqual(original.options.priority);
|
||||
expect(reconstructed.getCreationObject()).toEqual({ orderId: '12345' });
|
||||
});
|
||||
|
||||
tap.test('should serialize to JSON string and back with toJson/fromJson', async () => {
|
||||
const original = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'JSON Test',
|
||||
body: 'JSON Body'
|
||||
});
|
||||
|
||||
const jsonString = original.toJson();
|
||||
|
||||
// Should be valid JSON
|
||||
const parsed = JSON.parse(jsonString);
|
||||
expect(parsed.from).toEqual('sender@example.com');
|
||||
expect(parsed.subject).toEqual('JSON Test');
|
||||
|
||||
// Reconstruct from JSON string
|
||||
const reconstructed = smartmail.Smartmail.fromJson(jsonString);
|
||||
|
||||
expect(reconstructed.options.from).toEqual(original.options.from);
|
||||
expect(reconstructed.options.subject).toEqual(original.options.subject);
|
||||
expect(reconstructed.options.body).toEqual(original.options.body);
|
||||
});
|
||||
|
||||
tap.test('should serialize attachments to base64 and back', async () => {
|
||||
const testContent = 'Hello, this is test file content!';
|
||||
const testBuffer = Buffer.from(testContent);
|
||||
|
||||
const original = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Attachment Test',
|
||||
body: 'Test Body'
|
||||
});
|
||||
|
||||
// Create a SmartFile and add it as attachment
|
||||
const smartfile = new plugins.smartfile.SmartFile({
|
||||
path: 'test-file.txt',
|
||||
contentBuffer: testBuffer,
|
||||
base: './'
|
||||
});
|
||||
original.addAttachment(smartfile);
|
||||
|
||||
// Serialize to object
|
||||
const obj = original.toObject();
|
||||
|
||||
expect(obj.attachments.length).toEqual(1);
|
||||
expect(obj.attachments[0].filename).toEqual('test-file.txt');
|
||||
expect(obj.attachments[0].contentBase64).toEqual(testBuffer.toString('base64'));
|
||||
|
||||
// Reconstruct
|
||||
const reconstructed = smartmail.Smartmail.fromObject(obj);
|
||||
|
||||
expect(reconstructed.attachments.length).toEqual(1);
|
||||
expect(reconstructed.attachments[0].contentBuffer.toString()).toEqual(testContent);
|
||||
});
|
||||
|
||||
// Wire Protocol Message Types Tests
|
||||
tap.test('should have correct wire message type interfaces', async () => {
|
||||
// Test createMessageId and createTimestamp helpers
|
||||
const messageId = smartmail.createMessageId();
|
||||
const timestamp = smartmail.createTimestamp();
|
||||
|
||||
expect(typeof messageId).toEqual('string');
|
||||
expect(messageId.length).toBeGreaterThan(0);
|
||||
expect(typeof timestamp).toEqual('string');
|
||||
expect(timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
||||
});
|
||||
|
||||
// WireParser Tests
|
||||
tap.test('should parse and handle mail.send requests with WireParser', async () => {
|
||||
let receivedEmail: smartmail.Smartmail<any> | null = null;
|
||||
|
||||
const parser = new smartmail.WireParser({
|
||||
onMailSend: async (email, options) => {
|
||||
receivedEmail = email;
|
||||
return {
|
||||
type: 'mail.send.response',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
success: true,
|
||||
deliveryId: 'test-delivery-id'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const testEmail = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Wire Test',
|
||||
body: 'Wire Body'
|
||||
});
|
||||
|
||||
const request: smartmail.IMailSendRequest = {
|
||||
type: 'mail.send',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
email: testEmail.toObject()
|
||||
};
|
||||
|
||||
const response = await parser.handle(request) as smartmail.IMailSendResponse;
|
||||
|
||||
expect(response.type).toEqual('mail.send.response');
|
||||
expect(response.success).toBeTrue();
|
||||
expect(response.deliveryId).toEqual('test-delivery-id');
|
||||
expect(receivedEmail).not.toBeNull();
|
||||
expect(receivedEmail!.options.subject).toEqual('Wire Test');
|
||||
});
|
||||
|
||||
tap.test('should parse and handle settings.update requests with WireParser', async () => {
|
||||
let receivedSettings: smartmail.IWireSettings | null = null;
|
||||
|
||||
const parser = new smartmail.WireParser({
|
||||
onSettingsUpdate: async (settings) => {
|
||||
receivedSettings = settings;
|
||||
return {
|
||||
type: 'settings.update.response',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
success: true
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const request: smartmail.ISettingsUpdateRequest = {
|
||||
type: 'settings.update',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
settings: {
|
||||
smtp: {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: true,
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
defaultFrom: 'noreply@example.com',
|
||||
customSetting: 'custom-value'
|
||||
}
|
||||
};
|
||||
|
||||
const response = await parser.handle(request) as smartmail.ISettingsUpdateResponse;
|
||||
|
||||
expect(response.type).toEqual('settings.update.response');
|
||||
expect(response.success).toBeTrue();
|
||||
expect(receivedSettings).not.toBeNull();
|
||||
expect(receivedSettings!.smtp!.host).toEqual('smtp.example.com');
|
||||
expect(receivedSettings!.defaultFrom).toEqual('noreply@example.com');
|
||||
expect(receivedSettings!.customSetting).toEqual('custom-value');
|
||||
});
|
||||
|
||||
tap.test('should handle parseAndHandle convenience method', async () => {
|
||||
const parser = new smartmail.WireParser({
|
||||
onMailSend: async (email) => ({
|
||||
type: 'mail.send.response',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
success: true,
|
||||
deliveryId: 'convenience-test-id'
|
||||
})
|
||||
});
|
||||
|
||||
const testEmail = new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Convenience Test',
|
||||
body: 'Test Body'
|
||||
});
|
||||
|
||||
const requestJson = JSON.stringify({
|
||||
type: 'mail.send',
|
||||
messageId: smartmail.createMessageId(),
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
email: testEmail.toObject()
|
||||
});
|
||||
|
||||
const responseJson = await parser.parseAndHandle(requestJson);
|
||||
const response = JSON.parse(responseJson);
|
||||
|
||||
expect(response.type).toEqual('mail.send.response');
|
||||
expect(response.success).toBeTrue();
|
||||
expect(response.deliveryId).toEqual('convenience-test-id');
|
||||
});
|
||||
|
||||
tap.test('should return error response for unsupported handlers', async () => {
|
||||
const parser = new smartmail.WireParser({}); // No handlers
|
||||
|
||||
const request: smartmail.IMailSendRequest = {
|
||||
type: 'mail.send',
|
||||
messageId: 'test-msg-id',
|
||||
timestamp: smartmail.createTimestamp(),
|
||||
email: new smartmail.Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Test',
|
||||
body: 'Test'
|
||||
}).toObject()
|
||||
};
|
||||
|
||||
const response = await parser.handle(request) as smartmail.IMailSendResponse;
|
||||
|
||||
expect(response.type).toEqual('mail.send.response');
|
||||
expect(response.success).toBeFalse();
|
||||
expect(response.error).toEqual('Mail send not supported');
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user