e115934061
Publish `@librechat/data-schemas` to NPM / pack (push) Failing after 8s
Publish `@librechat/client` to NPM / pack (push) Failing after 0s
GitNexus Index / index (push) Failing after 1s
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Has been skipped
Sync Helm Chart Tags / Sync chart tags (push) Failing after 2s
Publish `librechat-data-provider` to NPM / pack (push) Failing after 1s
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Failing after 1s
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Failing after 0s
Sync Helm Chart Tags / Ignore non-main push (push) Has been skipped
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Failing after 1s
Publish `@librechat/client` to NPM / publish-npm (push) Has been cancelled
Publish `librechat-data-provider` to NPM / publish-npm (push) Has been cancelled
GitNexus Index / post-index (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / publish-npm (push) Has been cancelled
144 lines
4.8 KiB
JavaScript
144 lines
4.8 KiB
JavaScript
const nodemailer = require('nodemailer');
|
|
const { readFileAsString } = require('@librechat/api');
|
|
|
|
jest.mock('nodemailer');
|
|
jest.mock('@librechat/data-schemas', () => ({
|
|
logger: { debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
}));
|
|
jest.mock('@librechat/api', () => ({
|
|
logAxiosError: jest.fn(),
|
|
isEnabled: jest.fn((val) => val === 'true' || val === true),
|
|
readFileAsString: jest.fn(),
|
|
}));
|
|
|
|
const savedEnv = { ...process.env };
|
|
|
|
const mockSendMail = jest.fn().mockResolvedValue({ messageId: 'test-id' });
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
process.env = { ...savedEnv };
|
|
process.env.EMAIL_HOST = 'smtp.example.com';
|
|
process.env.EMAIL_PORT = '587';
|
|
process.env.EMAIL_FROM = 'noreply@example.com';
|
|
process.env.APP_TITLE = 'TestApp';
|
|
delete process.env.EMAIL_USERNAME;
|
|
delete process.env.EMAIL_PASSWORD;
|
|
delete process.env.MAILGUN_API_KEY;
|
|
delete process.env.MAILGUN_DOMAIN;
|
|
delete process.env.EMAIL_SERVICE;
|
|
delete process.env.EMAIL_ENCRYPTION;
|
|
delete process.env.EMAIL_ENCRYPTION_HOSTNAME;
|
|
delete process.env.EMAIL_ALLOW_SELFSIGNED;
|
|
|
|
readFileAsString.mockResolvedValue({ content: '<p>{{name}}</p>' });
|
|
nodemailer.createTransport.mockReturnValue({ sendMail: mockSendMail });
|
|
});
|
|
|
|
afterAll(() => {
|
|
process.env = savedEnv;
|
|
});
|
|
|
|
/** Loads a fresh copy of sendEmail so process.env reads are re-evaluated. */
|
|
function loadSendEmail() {
|
|
jest.resetModules();
|
|
jest.mock('nodemailer', () => ({
|
|
createTransport: jest.fn().mockReturnValue({ sendMail: mockSendMail }),
|
|
}));
|
|
jest.mock('@librechat/data-schemas', () => ({
|
|
logger: { debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
}));
|
|
jest.mock('@librechat/api', () => ({
|
|
logAxiosError: jest.fn(),
|
|
isEnabled: jest.fn((val) => val === 'true' || val === true),
|
|
readFileAsString: jest.fn().mockResolvedValue({ content: '<p>{{name}}</p>' }),
|
|
}));
|
|
return require('../sendEmail');
|
|
}
|
|
|
|
const baseParams = {
|
|
email: 'user@example.com',
|
|
subject: 'Test',
|
|
payload: { name: 'User' },
|
|
template: 'test.handlebars',
|
|
};
|
|
|
|
describe('sendEmail SMTP auth assembly', () => {
|
|
it('includes auth when both EMAIL_USERNAME and EMAIL_PASSWORD are set', async () => {
|
|
process.env.EMAIL_USERNAME = 'smtp_user';
|
|
process.env.EMAIL_PASSWORD = 'smtp_pass';
|
|
const sendEmail = loadSendEmail();
|
|
const { createTransport } = require('nodemailer');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
expect(createTransport).toHaveBeenCalledTimes(1);
|
|
const transporterOptions = createTransport.mock.calls[0][0];
|
|
expect(transporterOptions.auth).toEqual({
|
|
user: 'smtp_user',
|
|
pass: 'smtp_pass',
|
|
});
|
|
});
|
|
|
|
it('omits auth when both EMAIL_USERNAME and EMAIL_PASSWORD are absent', async () => {
|
|
const sendEmail = loadSendEmail();
|
|
const { createTransport } = require('nodemailer');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
expect(createTransport).toHaveBeenCalledTimes(1);
|
|
const transporterOptions = createTransport.mock.calls[0][0];
|
|
expect(transporterOptions.auth).toBeUndefined();
|
|
});
|
|
|
|
it('omits auth and logs a warning when only EMAIL_USERNAME is set', async () => {
|
|
process.env.EMAIL_USERNAME = 'smtp_user';
|
|
const sendEmail = loadSendEmail();
|
|
const { createTransport } = require('nodemailer');
|
|
const { logger: freshLogger } = require('@librechat/data-schemas');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
const transporterOptions = createTransport.mock.calls[0][0];
|
|
expect(transporterOptions.auth).toBeUndefined();
|
|
expect(freshLogger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining('EMAIL_USERNAME and EMAIL_PASSWORD must both be set'),
|
|
);
|
|
});
|
|
|
|
it('omits auth and logs a warning when only EMAIL_PASSWORD is set', async () => {
|
|
process.env.EMAIL_PASSWORD = 'smtp_pass';
|
|
const sendEmail = loadSendEmail();
|
|
const { createTransport } = require('nodemailer');
|
|
const { logger: freshLogger } = require('@librechat/data-schemas');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
const transporterOptions = createTransport.mock.calls[0][0];
|
|
expect(transporterOptions.auth).toBeUndefined();
|
|
expect(freshLogger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining('EMAIL_USERNAME and EMAIL_PASSWORD must both be set'),
|
|
);
|
|
});
|
|
|
|
it('does not log a warning when both credentials are properly set', async () => {
|
|
process.env.EMAIL_USERNAME = 'smtp_user';
|
|
process.env.EMAIL_PASSWORD = 'smtp_pass';
|
|
const sendEmail = loadSendEmail();
|
|
const { logger: freshLogger } = require('@librechat/data-schemas');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
expect(freshLogger.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not log a warning when both credentials are absent', async () => {
|
|
const sendEmail = loadSendEmail();
|
|
const { logger: freshLogger } = require('@librechat/data-schemas');
|
|
|
|
await sendEmail(baseParams);
|
|
|
|
expect(freshLogger.warn).not.toHaveBeenCalled();
|
|
});
|
|
});
|