mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
88979c852b
closes https://github.com/TryGhost/Team/issues/2388 We have seen examples of sites with member emails that have invalid characters that can cause an entire email send to fail, or just cause a failure to those addresses. The issue that allowed members with invalid email address to be saved was patched earlier, but its possible there are still sites that contain some of those invalid email addresses. This change updates new sending service to filter out the recipients with invalid email address before passing them to mail provider, so these rogue addresses don't affect the whole batch in anyway. We also trim the recipient emails to clear out any spaces first, which is the most likely culprit. - uses new email validator that detects invalid email addresses with special chars
207 lines
6.7 KiB
JavaScript
207 lines
6.7 KiB
JavaScript
const SendingService = require('../lib/sending-service');
|
||
const sinon = require('sinon');
|
||
const assert = require('assert');
|
||
|
||
describe('Sending service', function () {
|
||
describe('send', function () {
|
||
let emailProvider;
|
||
let emailRenderer;
|
||
let sendStub;
|
||
let replyTo;
|
||
|
||
beforeEach(function () {
|
||
sendStub = sinon.stub().resolves({
|
||
id: 'provider-123'
|
||
});
|
||
|
||
replyTo = 'ghost+reply@example.com';
|
||
|
||
emailRenderer = {
|
||
renderBody: sinon.stub().resolves({
|
||
html: '<html><body>Hi {{name}}</body></html>',
|
||
plaintext: 'Hi',
|
||
replacements: [
|
||
{
|
||
id: 'name',
|
||
token: '{{name}}',
|
||
getValue: (member) => {
|
||
return member.name;
|
||
}
|
||
}
|
||
]
|
||
}),
|
||
getSubject: sinon.stub().returns('Hi'),
|
||
getFromAddress: sinon.stub().returns('ghost@example.com'),
|
||
getReplyToAddress: () => {
|
||
return replyTo;
|
||
}
|
||
};
|
||
|
||
emailProvider = {
|
||
send: sendStub
|
||
};
|
||
});
|
||
|
||
afterEach(function () {
|
||
sinon.restore();
|
||
});
|
||
|
||
it('calls mailgun client with correct data', async function () {
|
||
const sendingService = new SendingService({
|
||
emailRenderer,
|
||
emailProvider
|
||
});
|
||
|
||
const response = await sendingService.send({
|
||
post: {},
|
||
newsletter: {},
|
||
segment: null,
|
||
emailId: '123',
|
||
members: [
|
||
{
|
||
email: 'member@example.com',
|
||
name: 'John'
|
||
}
|
||
]
|
||
}, {
|
||
clickTrackingEnabled: true,
|
||
openTrackingEnabled: true
|
||
});
|
||
assert.equal(response.id, 'provider-123');
|
||
sinon.assert.calledOnce(sendStub);
|
||
assert(sendStub.calledWith(
|
||
{
|
||
subject: 'Hi',
|
||
from: 'ghost@example.com',
|
||
replyTo: 'ghost+reply@example.com',
|
||
html: '<html><body>Hi {{name}}</body></html>',
|
||
plaintext: 'Hi',
|
||
emailId: '123',
|
||
replacementDefinitions: [
|
||
{
|
||
id: 'name',
|
||
token: '{{name}}',
|
||
getValue: sinon.match.func
|
||
}
|
||
],
|
||
recipients: [
|
||
{
|
||
email: 'member@example.com',
|
||
replacements: [{
|
||
id: 'name',
|
||
token: '{{name}}',
|
||
value: 'John'
|
||
}]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
clickTrackingEnabled: true,
|
||
openTrackingEnabled: true
|
||
}
|
||
));
|
||
});
|
||
|
||
it('removes invalid recipients before sending', async function () {
|
||
const sendingService = new SendingService({
|
||
emailRenderer,
|
||
emailProvider
|
||
});
|
||
|
||
const response = await sendingService.send({
|
||
post: {},
|
||
newsletter: {},
|
||
segment: null,
|
||
emailId: '123',
|
||
members: [
|
||
{
|
||
email: 'member@example.com',
|
||
name: 'John'
|
||
},
|
||
{
|
||
email: 'member+invalid@example.com<6F>',
|
||
name: 'John'
|
||
}
|
||
]
|
||
}, {
|
||
clickTrackingEnabled: true,
|
||
openTrackingEnabled: true
|
||
});
|
||
assert.equal(response.id, 'provider-123');
|
||
sinon.assert.calledOnce(sendStub);
|
||
assert(sendStub.calledWith(
|
||
{
|
||
subject: 'Hi',
|
||
from: 'ghost@example.com',
|
||
replyTo: 'ghost+reply@example.com',
|
||
html: '<html><body>Hi {{name}}</body></html>',
|
||
plaintext: 'Hi',
|
||
emailId: '123',
|
||
replacementDefinitions: [
|
||
{
|
||
id: 'name',
|
||
token: '{{name}}',
|
||
getValue: sinon.match.func
|
||
}
|
||
],
|
||
recipients: [
|
||
{
|
||
email: 'member@example.com',
|
||
replacements: [{
|
||
id: 'name',
|
||
token: '{{name}}',
|
||
value: 'John'
|
||
}]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
clickTrackingEnabled: true,
|
||
openTrackingEnabled: true
|
||
}
|
||
));
|
||
});
|
||
|
||
it('maps null replyTo to undefined', async function () {
|
||
const sendingService = new SendingService({
|
||
emailRenderer,
|
||
emailProvider
|
||
});
|
||
|
||
replyTo = null;
|
||
const response = await sendingService.send({
|
||
post: {},
|
||
newsletter: {},
|
||
segment: null,
|
||
emailId: '123',
|
||
members: [
|
||
{
|
||
email: 'member@example.com',
|
||
name: 'John'
|
||
}
|
||
]
|
||
}, {
|
||
clickTrackingEnabled: true,
|
||
openTrackingEnabled: true
|
||
});
|
||
assert.equal(response.id, 'provider-123');
|
||
sinon.assert.calledOnce(sendStub);
|
||
const firstCall = sendStub.getCall(0);
|
||
assert.equal(firstCall.args[0].replyTo, undefined);
|
||
});
|
||
});
|
||
|
||
describe('getMaximumRecipients', function () {
|
||
it('returns maximum recipients of email provider', function () {
|
||
const emailProvider = {
|
||
getMaximumRecipients: sinon.stub().returns(12)
|
||
};
|
||
const sendingService = new SendingService({
|
||
emailProvider
|
||
});
|
||
assert.equal(sendingService.getMaximumRecipients(), 12);
|
||
sinon.assert.calledOnce(emailProvider.getMaximumRecipients);
|
||
});
|
||
});
|
||
});
|