import { jest } from '@jest/globals'; jest.unstable_mockModule('../../../database/schemas/management/emailtemplate.schema.js', () => ({ emailTemplateModel: { modelName: 'emailTemplate' }, })); jest.unstable_mockModule('../../../database/database.js', () => ({ deleteObject: jest.fn(), editObject: jest.fn(), getObject: jest.fn(), getModelHistory: jest.fn(), getModelStats: jest.fn(), getObjectNeighbors: jest.fn(), getPropertyValues: jest.fn(), listObjects: jest.fn(), listObjectsByProperties: jest.fn(), newObject: jest.fn(), searchObjects: jest.fn(), })); jest.unstable_mockModule('../../../templates/templatemanager.js', () => ({ templateManager: { renderEmailTemplate: jest.fn() }, })); jest.unstable_mockModule('../../../templates/templateformatter.js', () => ({ formatTemplateContent: jest.fn((content) => ({ content })), })); jest.unstable_mockModule('log4js', () => ({ default: { getLogger: () => ({ level: 'info', debug: jest.fn(), error: jest.fn(), warn: jest.fn(), trace: jest.fn(), }), }, })); const { editEmailTemplateRouteHandler, getEmailTemplateNeighborsRouteHandler, getEmailTemplateStatsRouteHandler, listEmailTemplatesByPropertiesRouteHandler, newEmailTemplateRouteHandler, } = await import('../emailtemplates.js'); const { editObject, getModelStats, getObjectNeighbors, listObjectsByProperties, newObject } = await import('../../../database/database.js'); const ATTACHMENT_POPULATE = { path: 'attachments.file', strictPopulate: false }; describe('email template standard handlers', () => { let req; let res; beforeEach(() => { jest.clearAllMocks(); req = { query: {}, params: {}, body: {}, user: { _id: 'user-1' } }; res = { send: jest.fn(), status: jest.fn().mockReturnThis() }; }); it('supports active property lists and parent population', async () => { listObjectsByProperties.mockResolvedValue([{ name: 'Invoice', active: true }]); await listEmailTemplatesByPropertiesRouteHandler( req, res, ['name', 'active'], { active: true }, {} ); expect(listObjectsByProperties).toHaveBeenCalledWith( expect.objectContaining({ properties: ['name', 'active'], filter: { active: true }, populate: [ { path: 'parent', strictPopulate: false }, ATTACHMENT_POPULATE, ], }) ); }); it('returns stats and neighbors through generic database helpers', async () => { getModelStats.mockResolvedValue({ count: 2 }); getObjectNeighbors.mockResolvedValue({ previous: null, next: 'template-2' }); await getEmailTemplateStatsRouteHandler(req, res); await getEmailTemplateNeighborsRouteHandler( req, res, '', { active: true }, '', 'name', 'ascend', 'template-1' ); expect(getModelStats).toHaveBeenCalled(); expect(getObjectNeighbors).toHaveBeenCalledWith( expect.objectContaining({ id: 'template-1', filter: { active: true }, sort: 'name' }) ); }); it('persists normalized attachments on create and edit', async () => { const attachments = [ { type: 'documentTemplate', file: { _id: 'doc-1' }, fileName: 'Invoice', fileType: 'pdf', }, { type: 'file', file: 'file-1', fileName: 'Terms', }, ]; req.body = { name: 'Invoice', attachments }; newObject.mockResolvedValue({ _id: 'template-1' }); editObject.mockResolvedValue({ _id: 'template-1' }); await newEmailTemplateRouteHandler(req, res); expect(newObject).toHaveBeenCalledWith( expect.objectContaining({ newData: expect.objectContaining({ attachments: [ { _id: undefined, type: 'documentTemplate', file: 'doc-1', fileName: 'Invoice', fileType: 'pdf', }, { _id: undefined, type: 'file', file: 'file-1', fileName: 'Terms', fileType: undefined, }, ], }), }) ); req.params = { id: '507f1f77bcf86cd799439011' }; await editEmailTemplateRouteHandler(req, res); expect(editObject).toHaveBeenCalledWith( expect.objectContaining({ updateData: expect.objectContaining({ attachments: [ expect.objectContaining({ type: 'documentTemplate', file: 'doc-1', fileName: 'Invoice', fileType: 'pdf', }), expect.objectContaining({ type: 'file', file: 'file-1', fileName: 'Terms', }), ], }), populate: expect.arrayContaining([ATTACHMENT_POPULATE]), }) ); }); });