mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Fixed newsletter includes when adding or editing (#14696)
refs https://github.com/TryGhost/Team/issues/1571 refs https://ghost.slack.com/archives/C02G9E68C/p1650986988322609 - Makes sure the includes are always included - Moved read to the newsletter service - Added tests - Updated unit tests to work with multiple findOne calls - Fixed reject assertions not correctly awaiting in unit tests
This commit is contained in:
parent
366a7be36d
commit
38b9cf2472
5 changed files with 282 additions and 76 deletions
|
@ -1,11 +1,6 @@
|
|||
const models = require('../../models');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const allowedIncludes = ['count.posts', 'count.members'];
|
||||
|
||||
const messages = {
|
||||
newsletterNotFound: 'Newsletter not found.'
|
||||
};
|
||||
const newslettersService = require('../../services/newsletters');
|
||||
|
||||
module.exports = {
|
||||
|
@ -56,14 +51,7 @@ module.exports = {
|
|||
},
|
||||
permissions: true,
|
||||
async query(frame) {
|
||||
const newsletter = models.Newsletter.findOne(frame.data, frame.options);
|
||||
|
||||
if (!newsletter) {
|
||||
throw new errors.NotFoundError({
|
||||
message: tpl(messages.newsletterNotFound)
|
||||
});
|
||||
}
|
||||
return newsletter;
|
||||
return newslettersService.read(frame.data, frame.options);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ const tpl = require('@tryghost/tpl');
|
|||
const errors = require('@tryghost/errors');
|
||||
|
||||
const messages = {
|
||||
nameAlreadyExists: 'A newsletter with the same name already exists'
|
||||
nameAlreadyExists: 'A newsletter with the same name already exists',
|
||||
newsletterNotFound: 'Newsletter not found.'
|
||||
};
|
||||
|
||||
class NewslettersService {
|
||||
|
@ -79,6 +80,23 @@ class NewslettersService {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {Object} options data (id, uuid, slug...)
|
||||
* @param {Object} [options] options
|
||||
* @returns {Promise<object>} JSONified Newsletter models
|
||||
*/
|
||||
async read(data, options = {}) {
|
||||
const newsletter = await this.NewsletterModel.findOne(data, options);
|
||||
|
||||
if (!newsletter) {
|
||||
throw new errors.NotFoundError({
|
||||
message: tpl(messages.newsletterNotFound)
|
||||
});
|
||||
}
|
||||
return newsletter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {Object} [options] options
|
||||
|
@ -89,6 +107,7 @@ class NewslettersService {
|
|||
|
||||
return newsletters.toJSON();
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {object} attrs model properties
|
||||
|
@ -129,6 +148,9 @@ class NewslettersService {
|
|||
throw error;
|
||||
}
|
||||
|
||||
// Load relations correctly
|
||||
newsletter = await this.NewsletterModel.findOne({id: newsletter.id}, {...options, require: true});
|
||||
|
||||
// subscribe existing members if opt_in_existing=true
|
||||
if (options.opt_in_existing) {
|
||||
debug(`Subscribing members to newsletter '${newsletter.get('name')}'`);
|
||||
|
@ -153,12 +175,13 @@ class NewslettersService {
|
|||
/**
|
||||
* @public
|
||||
* @param {object} attrs model properties
|
||||
* @param {Object} [options] options
|
||||
* @param {Object} options options
|
||||
* @param {string} options.id Newsletter id to edit
|
||||
* @returns {Promise<{object}>} Newsetter Model with verification metadata
|
||||
*/
|
||||
async edit(attrs, options = {}) {
|
||||
async edit(attrs, options) {
|
||||
// fetch newsletter first so we can compare changed emails
|
||||
const originalNewsletter = await this.NewsletterModel.findOne(options, {require: true});
|
||||
const originalNewsletter = await this.NewsletterModel.findOne({id: options.id}, {require: true});
|
||||
|
||||
const {cleanedAttrs, emailsToVerify} = await this.prepAttrsForEmailVerification(attrs, originalNewsletter);
|
||||
|
||||
|
@ -176,8 +199,12 @@ class NewslettersService {
|
|||
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Load relations correctly in the response
|
||||
updatedNewsletter = await this.NewsletterModel.findOne({id: updatedNewsletter.id}, {...options, require: true});
|
||||
|
||||
return this.respondWithEmailVerification(updatedNewsletter, emailsToVerify);
|
||||
await this.respondWithEmailVerification(updatedNewsletter, emailsToVerify);
|
||||
return updatedNewsletter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,7 @@ Object {
|
|||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "new-newsletter-with-existing-members-subscribed",
|
||||
"sort_order": 7,
|
||||
"sort_order": 8,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
|
@ -75,7 +75,7 @@ Object {
|
|||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "my-test-newsletter-with-custom-sender_email",
|
||||
"sort_order": 6,
|
||||
"sort_order": 7,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
|
@ -128,7 +128,7 @@ Object {
|
|||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "my-test-newsletter-with-custom-sender_email-and-subscribe-existing",
|
||||
"sort_order": 8,
|
||||
"sort_order": 10,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
|
@ -145,7 +145,7 @@ exports[`Newsletters API Can add a newsletter - with custom sender_email and sub
|
|||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "826",
|
||||
"content-length": "827",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/newsletters\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
|
||||
|
@ -274,7 +274,7 @@ Object {
|
|||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "my-first-test-newsletter",
|
||||
"sort_order": 4,
|
||||
"sort_order": 5,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
|
@ -321,7 +321,7 @@ Object {
|
|||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "my-second-test-newsletter",
|
||||
"sort_order": 5,
|
||||
"sort_order": 6,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
|
@ -578,6 +578,57 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when adding a newsletter 1: [body] 1`] = `
|
||||
Object {
|
||||
"newsletters": Array [
|
||||
Object {
|
||||
"body_font_category": "serif",
|
||||
"count": Object {
|
||||
"members": 0,
|
||||
"posts": 0,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"description": null,
|
||||
"footer_content": null,
|
||||
"header_image": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": "My test newsletter 2",
|
||||
"sender_email": null,
|
||||
"sender_name": "Test",
|
||||
"sender_reply_to": "newsletter",
|
||||
"show_badge": true,
|
||||
"show_feature_image": true,
|
||||
"show_header_icon": true,
|
||||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "my-test-newsletter-2",
|
||||
"sort_order": 4,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": true,
|
||||
"title_alignment": "center",
|
||||
"title_font_category": "serif",
|
||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
"visibility": "members",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when adding a newsletter 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "688",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/newsletters\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
|
||||
"vary": "Origin, Accept-Encoding",
|
||||
"x-cache-invalidate": "/*",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when browsing newsletters 1: [body] 1`] = `
|
||||
Object {
|
||||
"meta": Object {
|
||||
|
@ -727,6 +778,56 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when editing newsletters 1: [body] 1`] = `
|
||||
Object {
|
||||
"newsletters": Array [
|
||||
Object {
|
||||
"body_font_category": "serif",
|
||||
"count": Object {
|
||||
"members": 4,
|
||||
"posts": 0,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"description": null,
|
||||
"footer_content": null,
|
||||
"header_image": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": "Updated newsletter name 2",
|
||||
"sender_email": "jamie@example.com",
|
||||
"sender_name": "Jamie",
|
||||
"sender_reply_to": "newsletter",
|
||||
"show_badge": true,
|
||||
"show_feature_image": true,
|
||||
"show_header_icon": true,
|
||||
"show_header_name": true,
|
||||
"show_header_title": true,
|
||||
"slug": "daily-newsletter",
|
||||
"sort_order": 1,
|
||||
"status": "active",
|
||||
"subscribe_on_signup": false,
|
||||
"title_alignment": "center",
|
||||
"title_font_category": "serif",
|
||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
"visibility": "members",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when editing newsletters 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "706",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Origin, Accept-Encoding",
|
||||
"x-cache-invalidate": "/*",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Newsletters API Can include members & posts counts when reading a newsletter 1: [body] 1`] = `
|
||||
Object {
|
||||
"newsletters": Array [
|
||||
|
|
|
@ -146,6 +146,36 @@ describe('Newsletters API', function () {
|
|||
header_image.should.equal(transformReadyPath);
|
||||
});
|
||||
|
||||
it('Can include members & posts counts when adding a newsletter', async function () {
|
||||
const newsletter = {
|
||||
uuid: uuid.v4(),
|
||||
name: 'My test newsletter 2',
|
||||
sender_name: 'Test',
|
||||
sender_email: null,
|
||||
sender_reply_to: 'newsletter',
|
||||
status: 'active',
|
||||
subscribe_on_signup: true,
|
||||
title_font_category: 'serif',
|
||||
body_font_category: 'serif',
|
||||
show_header_icon: true,
|
||||
show_header_title: true,
|
||||
show_badge: true,
|
||||
sort_order: 0
|
||||
};
|
||||
|
||||
await agent
|
||||
.post(`newsletters/?include=count.members,count.posts`)
|
||||
.body({newsletters: [newsletter]})
|
||||
.expectStatus(201)
|
||||
.matchBodySnapshot({
|
||||
newsletters: [newsletterSnapshot]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag,
|
||||
location: anyLocationFor('newsletters')
|
||||
});
|
||||
});
|
||||
|
||||
it('Can add multiple newsletters', async function () {
|
||||
const firstNewsletter = {
|
||||
name: 'My first test newsletter'
|
||||
|
@ -268,6 +298,23 @@ describe('Newsletters API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Can include members & posts counts when editing newsletters', async function () {
|
||||
const id = fixtureManager.get('newsletters', 0).id;
|
||||
await agent.put(`newsletters/${id}/?include=count.members,count.posts`)
|
||||
.body({
|
||||
newsletters: [{
|
||||
name: 'Updated newsletter name 2'
|
||||
}]
|
||||
})
|
||||
.expectStatus(200)
|
||||
.matchBodySnapshot({
|
||||
newsletters: [newsletterSnapshot]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag
|
||||
});
|
||||
});
|
||||
|
||||
it('Can edit a newsletters and update the sender_email when already set', async function () {
|
||||
const id = fixtureManager.get('newsletters', 0).id;
|
||||
|
||||
|
@ -359,8 +406,44 @@ describe('Newsletters API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it(`Can't add multiple newsletters with same name`, async function () {
|
||||
const firstNewsletter = {
|
||||
name: 'Duplicate newsletter'
|
||||
};
|
||||
|
||||
const secondNewsletter = {...firstNewsletter};
|
||||
|
||||
await agent
|
||||
.post(`newsletters/`)
|
||||
.body({newsletters: [firstNewsletter]})
|
||||
.expectStatus(201)
|
||||
.matchBodySnapshot({
|
||||
newsletters: [newsletterSnapshotWithoutSortOrder]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag,
|
||||
location: anyLocationFor('newsletters')
|
||||
});
|
||||
|
||||
await agent
|
||||
.post(`newsletters/`)
|
||||
.body({newsletters: [secondNewsletter]})
|
||||
.expectStatus(422)
|
||||
.matchBodySnapshot({
|
||||
errors: [{
|
||||
id: anyUuid,
|
||||
message: 'Validation error, cannot save newsletter.',
|
||||
context: 'A newsletter with the same name already exists'
|
||||
}]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag
|
||||
});
|
||||
});
|
||||
|
||||
it('Can add a newsletter - with custom sender_email and subscribe existing members', async function () {
|
||||
if (DatabaseInfo.isSQLite(db.knex)) {
|
||||
// This breaks snapshot tests if you don't update snapshot tests on MySQL + make sure this is the last ADD test
|
||||
return;
|
||||
}
|
||||
const newsletter = {
|
||||
|
@ -399,41 +482,6 @@ describe('Newsletters API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it(`Can't add multiple newsletters with same name`, async function () {
|
||||
const firstNewsletter = {
|
||||
name: 'Duplicate newsletter'
|
||||
};
|
||||
|
||||
const secondNewsletter = {...firstNewsletter};
|
||||
|
||||
await agent
|
||||
.post(`newsletters/`)
|
||||
.body({newsletters: [firstNewsletter]})
|
||||
.expectStatus(201)
|
||||
.matchBodySnapshot({
|
||||
newsletters: [newsletterSnapshotWithoutSortOrder]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag,
|
||||
location: anyLocationFor('newsletters')
|
||||
});
|
||||
|
||||
await agent
|
||||
.post(`newsletters/`)
|
||||
.body({newsletters: [secondNewsletter]})
|
||||
.expectStatus(422)
|
||||
.matchBodySnapshot({
|
||||
errors: [{
|
||||
id: anyUuid,
|
||||
message: 'Validation error, cannot save newsletter.',
|
||||
context: 'A newsletter with the same name already exists'
|
||||
}]
|
||||
})
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag
|
||||
});
|
||||
});
|
||||
|
||||
it(`Can't edit multiple newsletters to existing name`, async function () {
|
||||
const id = fixtureManager.get('newsletters', 0).id;
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ describe('NewslettersService', function () {
|
|||
|
||||
beforeEach(function () {
|
||||
getStub = sinon.stub();
|
||||
getStub.withArgs('id').returns('test');
|
||||
sinon.spy(tokenProvider, 'create');
|
||||
sinon.spy(tokenProvider, 'validate');
|
||||
mockManager.mockMail();
|
||||
|
@ -56,6 +57,26 @@ describe('NewslettersService', function () {
|
|||
mockManager.restore();
|
||||
});
|
||||
|
||||
describe('read', function () {
|
||||
let findOneStub;
|
||||
beforeEach(function () {
|
||||
// Stub edit as a function that returns its first argument
|
||||
findOneStub = sinon.stub(models.Newsletter, 'findOne').returns({get: getStub, id: 'test'});
|
||||
});
|
||||
|
||||
it('returns the result of findOne', async function () {
|
||||
const result = await newsletterService.read({slug: 'my-slug'});
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {slug: 'my-slug'}, {});
|
||||
assert.equal(result.id, 'test');
|
||||
});
|
||||
|
||||
it('throws a not found error if not found', async function () {
|
||||
findOneStub = findOneStub.returns(null);
|
||||
await assert.rejects(newsletterService.read({slug: 'my-slug'}), {errorType: 'NotFoundError'});
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {slug: 'my-slug'}, {});
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO replace this with a specific function for fetching all available newsletters
|
||||
describe('browse', function () {
|
||||
it('lists all newsletters by calling findAll and toJSON', async function () {
|
||||
|
@ -70,15 +91,16 @@ describe('NewslettersService', function () {
|
|||
});
|
||||
|
||||
describe('add', function () {
|
||||
let addStub, fetchMembersStub, fakeMemberIds, subscribeStub, getNextAvailableSortOrderStub;
|
||||
let addStub, fetchMembersStub, fakeMemberIds, subscribeStub, getNextAvailableSortOrderStub, findOneStub;
|
||||
beforeEach(function () {
|
||||
fakeMemberIds = new Array(3).fill({id: 1});
|
||||
subscribeStub = sinon.stub().returns(fakeMemberIds);
|
||||
|
||||
// Stub add as a function that returns get & subscribeMembersById methods
|
||||
addStub = sinon.stub(models.Newsletter, 'add').returns({get: getStub, subscribeMembersById: subscribeStub});
|
||||
addStub = sinon.stub(models.Newsletter, 'add').returns({get: getStub, id: 'test', subscribeMembersById: subscribeStub});
|
||||
fetchMembersStub = sinon.stub(models.Member, 'fetchAllSubscribed').returns([]);
|
||||
getNextAvailableSortOrderStub = sinon.stub(models.Newsletter, 'getNextAvailableSortOrder').returns(1);
|
||||
findOneStub = sinon.stub(models.Newsletter, 'findOne').returns({get: getStub, id: 'test', subscribeMembersById: subscribeStub});
|
||||
});
|
||||
|
||||
it('rejects if the limit services determines it would be over the limit', async function () {
|
||||
|
@ -98,10 +120,11 @@ describe('NewslettersService', function () {
|
|||
});
|
||||
|
||||
it('rejects if called with no data', async function () {
|
||||
assert.rejects(await newsletterService.add, {name: 'TypeError'});
|
||||
await assert.rejects(newsletterService.add(), {name: 'TypeError'});
|
||||
sinon.assert.notCalled(addStub);
|
||||
sinon.assert.notCalled(getNextAvailableSortOrderStub);
|
||||
sinon.assert.notCalled(fetchMembersStub);
|
||||
sinon.assert.notCalled(findOneStub);
|
||||
});
|
||||
|
||||
it('will attempt to add empty object without verification', async function () {
|
||||
|
@ -111,6 +134,7 @@ describe('NewslettersService', function () {
|
|||
sinon.assert.calledOnce(getNextAvailableSortOrderStub);
|
||||
sinon.assert.notCalled(fetchMembersStub);
|
||||
sinon.assert.calledOnceWithExactly(addStub, {sort_order: 1}, {});
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {id: 'test'}, {require: true});
|
||||
});
|
||||
|
||||
it('will override sort_order', async function () {
|
||||
|
@ -122,6 +146,7 @@ describe('NewslettersService', function () {
|
|||
sinon.assert.calledOnce(getNextAvailableSortOrderStub);
|
||||
sinon.assert.notCalled(fetchMembersStub);
|
||||
sinon.assert.calledOnceWithExactly(addStub, {name: 'hello world', sort_order: 1}, options);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {id: 'test'}, {foo: 'bar', require: true});
|
||||
});
|
||||
|
||||
it('will pass object and options through to model when there are no fields needing verification', async function () {
|
||||
|
@ -133,6 +158,7 @@ describe('NewslettersService', function () {
|
|||
assert.equal(result.meta, undefined); // meta property has not been added
|
||||
sinon.assert.calledOnceWithExactly(addStub, data, options);
|
||||
sinon.assert.notCalled(fetchMembersStub);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {id: 'test'}, {foo: 'bar', require: true});
|
||||
});
|
||||
|
||||
it('will trigger verification when sender_email is provided', async function () {
|
||||
|
@ -148,8 +174,9 @@ describe('NewslettersService', function () {
|
|||
});
|
||||
sinon.assert.calledOnceWithExactly(addStub, {name: 'hello world', sort_order: 1}, options);
|
||||
mockManager.assert.sentEmail({to: 'test@example.com'});
|
||||
sinon.assert.calledOnceWithExactly(tokenProvider.create, {id: undefined, property: 'sender_email', value: 'test@example.com'});
|
||||
sinon.assert.calledOnceWithExactly(tokenProvider.create, {id: 'test', property: 'sender_email', value: 'test@example.com'});
|
||||
sinon.assert.notCalled(fetchMembersStub);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {id: 'test'}, {foo: 'bar', require: true});
|
||||
});
|
||||
|
||||
it('will try to find existing members when opt_in_existing is provided', async function () {
|
||||
|
@ -165,6 +192,7 @@ describe('NewslettersService', function () {
|
|||
sinon.assert.calledOnceWithExactly(addStub, {name: 'hello world', sort_order: 1}, options);
|
||||
mockManager.assert.sentEmailCount(0);
|
||||
sinon.assert.calledOnce(fetchMembersStub);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, {id: 'test'}, {opt_in_existing: true, transacting: options.transacting, require: true});
|
||||
});
|
||||
|
||||
it('will try to subscribe existing members when opt_in_existing provided + members exist', async function () {
|
||||
|
@ -190,36 +218,42 @@ describe('NewslettersService', function () {
|
|||
let editStub, findOneStub;
|
||||
beforeEach(function () {
|
||||
// Stub edit as a function that returns its first argument
|
||||
editStub = sinon.stub(models.Newsletter, 'edit').returns({get: getStub});
|
||||
findOneStub = sinon.stub(models.Newsletter, 'findOne').returns({get: getStub});
|
||||
editStub = sinon.stub(models.Newsletter, 'edit').returns({get: getStub, id: 'test'});
|
||||
findOneStub = sinon.stub(models.Newsletter, 'findOne').returns({get: getStub, id: 'test'});
|
||||
});
|
||||
|
||||
it('rejects if called with no data', async function () {
|
||||
assert.rejects(await newsletterService.add, {name: 'TypeError'});
|
||||
await assert.rejects(newsletterService.add(), {name: 'TypeError'});
|
||||
sinon.assert.notCalled(editStub);
|
||||
});
|
||||
|
||||
it('will attempt to add empty object without verification', async function () {
|
||||
const result = await newsletterService.edit({});
|
||||
const result = await newsletterService.edit({}, {id: 'test'});
|
||||
|
||||
assert.equal(result.meta, undefined); // meta property has not been added
|
||||
sinon.assert.calledOnceWithExactly(editStub, {}, {});
|
||||
sinon.assert.calledOnceWithExactly(editStub, {}, {id: 'test'});
|
||||
});
|
||||
|
||||
it('will pass object and options through to model when there are no fields needing verification', async function () {
|
||||
const data = {name: 'hello world'};
|
||||
const options = {foo: 'bar'};
|
||||
const options = {foo: 'bar', id: 'test'};
|
||||
|
||||
const result = await newsletterService.edit(data, options);
|
||||
|
||||
assert.equal(result.meta, undefined); // meta property has not been added
|
||||
sinon.assert.calledOnceWithExactly(editStub, data, options);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, options, {require: true});
|
||||
|
||||
sinon.assert.calledTwice(findOneStub);
|
||||
sinon.assert.calledWithExactly(findOneStub.firstCall, {id: 'test'}, {require: true});
|
||||
sinon.assert.calledWithExactly(findOneStub.secondCall, {id: 'test'}, {...options, require: true});
|
||||
});
|
||||
|
||||
it('will trigger verification when sender_email is provided', async function () {
|
||||
const data = {name: 'hello world', sender_email: 'test@example.com'};
|
||||
const options = {foo: 'bar'};
|
||||
const options = {id: 'test', foo: 'bar'};
|
||||
|
||||
// Explicitly set the old value to a different value
|
||||
getStub.withArgs('sender_email').returns('old@example.com');
|
||||
|
||||
const result = await newsletterService.edit(data, options);
|
||||
|
||||
|
@ -229,14 +263,18 @@ describe('NewslettersService', function () {
|
|||
]
|
||||
});
|
||||
sinon.assert.calledOnceWithExactly(editStub, {name: 'hello world'}, options);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, options, {require: true});
|
||||
|
||||
sinon.assert.calledTwice(findOneStub);
|
||||
sinon.assert.calledWithExactly(findOneStub.firstCall, {id: 'test'}, {require: true});
|
||||
sinon.assert.calledWithExactly(findOneStub.secondCall, {id: 'test'}, {...options, require: true});
|
||||
|
||||
mockManager.assert.sentEmail({to: 'test@example.com'});
|
||||
sinon.assert.calledOnceWithExactly(tokenProvider.create, {id: undefined, property: 'sender_email', value: 'test@example.com'});
|
||||
sinon.assert.calledOnceWithExactly(tokenProvider.create, {id: 'test', property: 'sender_email', value: 'test@example.com'});
|
||||
});
|
||||
|
||||
it('will NOT trigger verification when sender_email is provided but is already verified', async function () {
|
||||
const data = {name: 'hello world', sender_email: 'test@example.com'};
|
||||
const options = {foo: 'bar'};
|
||||
const options = {foo: 'bar', id: 'test'};
|
||||
|
||||
// The model says this is already verified
|
||||
getStub.withArgs('sender_email').returns('test@example.com');
|
||||
|
@ -245,7 +283,11 @@ describe('NewslettersService', function () {
|
|||
|
||||
assert.deepEqual(result.meta, undefined);
|
||||
sinon.assert.calledOnceWithExactly(editStub, {name: 'hello world', sender_email: 'test@example.com'}, options);
|
||||
sinon.assert.calledOnceWithExactly(findOneStub, options, {require: true});
|
||||
|
||||
sinon.assert.calledTwice(findOneStub);
|
||||
sinon.assert.calledWithExactly(findOneStub.firstCall, {id: 'test'}, {require: true});
|
||||
sinon.assert.calledWithExactly(findOneStub.secondCall, {id: 'test'}, {...options, require: true});
|
||||
|
||||
mockManager.assert.sentEmailCount(0);
|
||||
});
|
||||
});
|
||||
|
@ -259,7 +301,7 @@ describe('NewslettersService', function () {
|
|||
});
|
||||
|
||||
it('rejects if called with no data', async function () {
|
||||
assert.rejects(await newsletterService.verifyPropertyUpdate, {name: 'TypeError'});
|
||||
await assert.rejects(newsletterService.verifyPropertyUpdate(), {name: 'SyntaxError'});
|
||||
});
|
||||
|
||||
it('Updates model with values from token', async function () {
|
||||
|
|
Loading…
Reference in a new issue