From 879aad263d1291c904ed6871cce7bb24d22463bd Mon Sep 17 00:00:00 2001 From: Naz Date: Mon, 5 Dec 2022 18:24:28 +0700 Subject: [PATCH] Added email verification trigger test refs https://github.com/TryGhost/Toolbox/issues/476 - The email verification trigger and host settings related bugs have been a cause of bugs in past releases. The admin client verification source did not have any test coverage in the past. - The members test suite size is getting out of hand. This test is quite verbose, because of the state it's trying to check. - In the future we should consider splitting up Member API (and probably other) test suites into smaller pieces. --- .../admin/__snapshots__/members.test.js.snap | 220 ++++++++++++++++++ ghost/core/test/e2e-api/admin/members.test.js | 73 +++++- 2 files changed, 291 insertions(+), 2 deletions(-) diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap index 876481f454..4a4715f961 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap @@ -693,6 +693,226 @@ Object { } `; +exports[`Members API Can add a member and trigger host email verification limits 1: [body] 1`] = ` +Object { + "members": Array [ + Object { + "attribution": Object { + "id": null, + "referrer_medium": "Ghost Admin", + "referrer_source": "Created manually", + "referrer_url": null, + "title": null, + "type": "url", + "url": null, + }, + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "memberPassVerifivation@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "pass verification", + "newsletters": Any, + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "tiers": Array [], + "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\\}/, + }, + ], +} +`; + +exports[`Members API Can add a member and trigger host email verification limits 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": "2080", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/members\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Members API Can add a member and trigger host email verification limits 3: [body] 1`] = ` +Object { + "members": Array [ + Object { + "attribution": Object { + "id": null, + "referrer_medium": "Ghost Admin", + "referrer_source": "Created manually", + "referrer_url": null, + "title": null, + "type": "url", + "url": null, + }, + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "memberFailVerifivation@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "fail verification", + "newsletters": Any, + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "tiers": Array [], + "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\\}/, + }, + ], +} +`; + +exports[`Members API Can add a member and trigger host email verification limits 4: [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": "2080", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/members\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Members API Can add a member and trigger host limit 1: [body] 1`] = ` +Object { + "members": Array [ + Object { + "attribution": Object { + "id": null, + "referrer_medium": "Ghost Admin", + "referrer_source": "Created manually", + "referrer_url": null, + "title": null, + "type": "url", + "url": null, + }, + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "memberPassVerifivation@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "pass verification", + "newsletters": Any, + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "tiers": Array [], + "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\\}/, + }, + ], +} +`; + +exports[`Members API Can add a member and trigger host limit 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": "2080", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/members\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Members API Can add a member and trigger host limit 3: [body] 1`] = ` +Object { + "members": Array [ + Object { + "attribution": Object { + "id": null, + "referrer_medium": "Ghost Admin", + "referrer_source": "Created manually", + "referrer_url": null, + "title": null, + "type": "url", + "url": null, + }, + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "memberFailVerifivation@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "fail verification", + "newsletters": Any, + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "tiers": Array [], + "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\\}/, + }, + ], +} +`; + +exports[`Members API Can add a member and trigger host limit 4: [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": "2080", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/members\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + exports[`Members API Can add a member that is not subscribed (old) 1: [body] 1`] = ` Object { "members": Array [ diff --git a/ghost/core/test/e2e-api/admin/members.test.js b/ghost/core/test/e2e-api/admin/members.test.js index 00168aabfa..94cb3c2260 100644 --- a/ghost/core/test/e2e-api/admin/members.test.js +++ b/ghost/core/test/e2e-api/admin/members.test.js @@ -1,4 +1,4 @@ -const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework'); +const {agentProvider, mockManager, fixtureManager, matchers, sleep} = require('../../utils/e2e-framework'); const {anyEtag, anyObjectId, anyUuid, anyISODateTime, anyISODate, anyString, anyArray, anyLocationFor, anyContentLength, anyErrorId, anyObject} = matchers; const ObjectId = require('bson-objectid').default; @@ -7,6 +7,8 @@ const nock = require('nock'); const should = require('should'); const sinon = require('sinon'); const testUtils = require('../../utils'); +const configUtils = require('../../utils/configUtils'); + const Papa = require('papaparse'); const models = require('../../../core/server/models'); @@ -14,9 +16,11 @@ const membersService = require('../../../core/server/services/members'); const memberAttributionService = require('../../../core/server/services/member-attribution'); const urlService = require('../../../core/server/services/url'); const urlUtils = require('../../../core/shared/url-utils'); +const {_updateVerificationTrigger} = require('../../../core/server/services/members'); +const settingsCache = require('../../../core/shared/settings-cache'); /** - * Assert that haystack and needles match, ignoring the order. + * Assert that haystack and needles match, ignoring the order. */ function matchArrayWithoutOrder(haystack, needles) { // Order shouldn't matter here @@ -700,6 +704,71 @@ describe('Members API', function () { }); }); + it('Can add a member and trigger host email verification limits', async function () { + configUtils.set('hostSettings:emailVerification', { + apiThreshold: 0, + adminThreshold: 1, + importThreshold: 0, + verified: false, + escalationAddress: 'test@example.com' + }); + _updateVerificationTrigger(); + + assert.ok(!settingsCache.get('email_verification_required'), 'Email verification should NOT be required'); + + const member = { + name: 'pass verification', + email: 'memberPassVerifivation@test.com' + }; + + const {body: passBody} = await agent + .post(`/members/`) + .body({members: [member]}) + .expectStatus(201) + .matchBodySnapshot({ + members: new Array(1).fill(memberMatcherShallowIncludes) + }) + .matchHeaderSnapshot({ + etag: anyEtag, + location: anyLocationFor('members') + }); + const memberPassVerification = passBody.members[0]; + + await sleep(100); + assert.ok(!settingsCache.get('email_verification_required'), 'Email verification should NOT be required'); + + const memberFailLimit = { + name: 'fail verification', + email: 'memberFailVerifivation@test.com' + }; + + const {body: failBody} = await agent + .post(`/members/`) + .body({members: [memberFailLimit]}) + .expectStatus(201) + .matchBodySnapshot({ + members: new Array(1).fill(memberMatcherShallowIncludes) + }) + .matchHeaderSnapshot({ + etag: anyEtag, + location: anyLocationFor('members') + }); + const memberFailVerification = failBody.members[0]; + + await sleep(100); + assert.ok(settingsCache.get('email_verification_required'), 'Email verification should be required'); + mockManager.assert.sentEmail({ + subject: 'Email needs verification' + }); + + // state cleanup + await agent.delete(`/members/${memberPassVerification.id}`); + await agent.delete(`/members/${memberFailVerification.id}`); + + configUtils.restore(); + _updateVerificationTrigger(); + }); + it('Can add and send a signup confirmation email', async function () { const member = { name: 'Send Me Confirmation',