168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { TypedRequest } from '@api.global/typedrequest';
|
|
import { DcRouter } from '../ts/index.js';
|
|
import * as interfaces from '../ts_interfaces/index.js';
|
|
|
|
const TEST_PORT = 3201;
|
|
const BASE_URL = `http://localhost:${TEST_PORT}/typedrequest`;
|
|
|
|
let testDcRouter: DcRouter;
|
|
let adminIdentity: interfaces.data.IIdentity;
|
|
let removedQueueItemId: string | undefined;
|
|
let lastEnqueueArgs: any[] | undefined;
|
|
|
|
const queueItems = [
|
|
{
|
|
id: 'failed-email-1',
|
|
status: 'failed',
|
|
attempts: 3,
|
|
nextAttempt: new Date('2026-04-14T10:00:00.000Z'),
|
|
lastError: '550 mailbox unavailable',
|
|
processingMode: 'mta',
|
|
route: undefined,
|
|
createdAt: new Date('2026-04-14T09:00:00.000Z'),
|
|
processingResult: {
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.net'],
|
|
cc: ['copy@example.net'],
|
|
subject: 'Older message',
|
|
text: 'hello',
|
|
headers: { 'x-test': '1' },
|
|
getMessageId: () => 'message-older',
|
|
getAttachmentsSize: () => 64,
|
|
},
|
|
},
|
|
{
|
|
id: 'delivered-email-1',
|
|
status: 'delivered',
|
|
attempts: 1,
|
|
processingMode: 'mta',
|
|
route: undefined,
|
|
createdAt: new Date('2026-04-14T11:00:00.000Z'),
|
|
processingResult: {
|
|
email: {
|
|
from: 'fresh@example.com',
|
|
to: ['new@example.net'],
|
|
cc: [],
|
|
subject: 'Newest message',
|
|
},
|
|
html: '<p>newest</p>',
|
|
text: 'newest',
|
|
headers: { 'x-fresh': 'true' },
|
|
getMessageId: () => 'message-newer',
|
|
getAttachmentsSize: () => 0,
|
|
},
|
|
},
|
|
];
|
|
|
|
tap.test('should start DCRouter with OpsServer for email API tests', async () => {
|
|
testDcRouter = new DcRouter({
|
|
opsServerPort: TEST_PORT,
|
|
dbConfig: { enabled: false },
|
|
});
|
|
|
|
await testDcRouter.start();
|
|
testDcRouter.emailServer = {
|
|
getQueueItems: () => [...queueItems],
|
|
getQueueItem: (id: string) => queueItems.find((item) => item.id === id),
|
|
getQueueStats: () => ({
|
|
queueSize: 2,
|
|
status: {
|
|
pending: 0,
|
|
processing: 1,
|
|
failed: 1,
|
|
deferred: 1,
|
|
delivered: 1,
|
|
},
|
|
}),
|
|
deliveryQueue: {
|
|
enqueue: async (...args: any[]) => {
|
|
lastEnqueueArgs = args;
|
|
return 'resent-queue-id';
|
|
},
|
|
removeItem: async (id: string) => {
|
|
removedQueueItemId = id;
|
|
return true;
|
|
},
|
|
},
|
|
} as any;
|
|
|
|
expect(testDcRouter.opsServer).toBeInstanceOf(Object);
|
|
});
|
|
|
|
tap.test('should login as admin for email API tests', async () => {
|
|
const loginRequest = new TypedRequest<interfaces.requests.IReq_AdminLoginWithUsernameAndPassword>(
|
|
BASE_URL,
|
|
'adminLoginWithUsernameAndPassword',
|
|
);
|
|
|
|
const response = await loginRequest.fire({
|
|
username: 'admin',
|
|
password: 'admin',
|
|
});
|
|
|
|
adminIdentity = response.identity;
|
|
expect(adminIdentity.jwt).toBeTruthy();
|
|
});
|
|
|
|
tap.test('should return queued emails through the email ops API', async () => {
|
|
const request = new TypedRequest<interfaces.requests.IReq_GetAllEmails>(BASE_URL, 'getAllEmails');
|
|
const response = await request.fire({
|
|
identity: adminIdentity,
|
|
});
|
|
|
|
expect(response.emails.map((email) => email.id)).toEqual(['delivered-email-1', 'failed-email-1']);
|
|
expect(response.emails[0].status).toEqual('delivered');
|
|
expect(response.emails[1].status).toEqual('bounced');
|
|
});
|
|
|
|
tap.test('should return email detail through the email ops API', async () => {
|
|
const request = new TypedRequest<interfaces.requests.IReq_GetEmailDetail>(BASE_URL, 'getEmailDetail');
|
|
const response = await request.fire({
|
|
identity: adminIdentity,
|
|
emailId: 'failed-email-1',
|
|
});
|
|
|
|
expect(response.email?.toList).toEqual(['recipient@example.net']);
|
|
expect(response.email?.cc).toEqual(['copy@example.net']);
|
|
expect(response.email?.rejectionReason).toEqual('550 mailbox unavailable');
|
|
expect(response.email?.headers).toEqual({ 'x-test': '1' });
|
|
});
|
|
|
|
tap.test('should expose queue status through the stats API', async () => {
|
|
const request = new TypedRequest<interfaces.requests.IReq_GetQueueStatus>(BASE_URL, 'getQueueStatus');
|
|
const response = await request.fire({
|
|
identity: adminIdentity,
|
|
});
|
|
|
|
expect(response.queues.length).toEqual(1);
|
|
expect(response.queues[0].size).toEqual(0);
|
|
expect(response.queues[0].processing).toEqual(1);
|
|
expect(response.queues[0].failed).toEqual(1);
|
|
expect(response.queues[0].retrying).toEqual(1);
|
|
expect(response.totalItems).toEqual(3);
|
|
});
|
|
|
|
tap.test('should resend failed email through the admin email ops API', async () => {
|
|
const request = new TypedRequest<interfaces.requests.IReq_ResendEmail>(BASE_URL, 'resendEmail');
|
|
const response = await request.fire({
|
|
identity: adminIdentity,
|
|
emailId: 'failed-email-1',
|
|
});
|
|
|
|
expect(response.success).toEqual(true);
|
|
expect(response.newQueueId).toEqual('resent-queue-id');
|
|
expect(removedQueueItemId).toEqual('failed-email-1');
|
|
expect(lastEnqueueArgs?.[0]).toEqual(queueItems[0].processingResult);
|
|
});
|
|
|
|
tap.test('should stop DCRouter after email API tests', async () => {
|
|
await testDcRouter.stop();
|
|
});
|
|
|
|
tap.test('cleanup', async () => {
|
|
await tap.stopForcefully();
|
|
});
|
|
|
|
export default tap.start();
|