From 02953490a5ea5907abce9d1672c5ac748819e2bc Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Fri, 3 Feb 2023 10:45:50 -0600 Subject: [PATCH] added mention sending service e2e tests (#16226) refs TryGhost/Team#2508 -added sending service e2e tests -should job off this sending service for better tests -and for ghost to finish processing the job before shutdown --- .../test/e2e-server/services/mentions.test.js | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 ghost/core/test/e2e-server/services/mentions.test.js diff --git a/ghost/core/test/e2e-server/services/mentions.test.js b/ghost/core/test/e2e-server/services/mentions.test.js new file mode 100644 index 0000000000..bf915ba6c2 --- /dev/null +++ b/ghost/core/test/e2e-server/services/mentions.test.js @@ -0,0 +1,238 @@ +const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework'); +const sinon = require('sinon'); +const nock = require('nock'); +const assert = require('assert'); +const sleep = require('../../utils/sleep'); +const markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc; +const dnsPromises = require('dns').promises; + +let agent; +let mentionUrl = new URL('https://www.otherghostsite.com/'); +let mentionHtml = `Check out this really cool other site.`; +let endpointUrl = new URL('https://www.endpoint.com/'); +let targetHtml = `Some content`; +let mentionMock; +let endpointMock; + +const mentionsPost = { + title: 'testing sending webmentions', + mobiledoc: markdownToMobiledoc(mentionHtml) +}; + +// NOTE: we need to sleep after the API calls because there's a race condition between the nock'd http response and the mentions service +// TODO: move the mentions service to a jobbed process so we can await it + +describe('Mentions Service', function () { + before(async function () { + agent = await agentProvider.getAdminAPIAgent(); + await fixtureManager.init('users'); + await agent.loginAsAdmin(); + nock.disableNetConnect(); // make sure we don't actually send mentions + }); + + beforeEach(async function () { + await mockManager.mockLabsEnabled('webmentions'); + + // externalRequest does dns lookup; stub to make sure we don't fail with fake domain names + sinon.stub(dnsPromises, 'lookup').callsFake(function () { + return Promise.resolve({address: '123.123.123.123'}); + }); + + // mock response from website mentioned by post to provide endpoint + mentionMock = nock(mentionUrl.href) + .get('/') + .reply(200, targetHtml, {'content-type': 'text/html'}); + + // mock response from mention endpoint, usually 201, sometimes 202 + endpointMock = nock(endpointUrl.href) + .post('/') + .reply(201); + }); + + afterEach(function () { + mockManager.restore(); + nock.cleanAll(); + }); + + after(function () { + nock.enableNetConnect(); + nock.cleanAll(); + }); + + describe('Sending Service', function () { + describe(`does not send when we expect it to not send`, function () { + it('New draft post created', async function () { + let publishedPost = {status: 'draft', ...mentionsPost}; + await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + assert.equal(mentionMock.isDone(), false); + assert.equal(endpointMock.isDone(), false); + }); + + it('Respects the disabled flag', async function () { + await mockManager.mockLabsDisabled('webmentions'); + let publishedPost = {status: 'published', ...mentionsPost}; + await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + assert.equal(mentionMock.isDone(), false); + assert.equal(endpointMock.isDone(), false); + }); + + it('Email only post published', async function () { + let publishedPost = {status: 'published', email_only: true, ...mentionsPost}; + await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + assert.equal(mentionMock.isDone(), false); + assert.equal(endpointMock.isDone(), false); + }); + }); + + describe(`does send when we expect it to send`, function () { + it('Newly published post (post.published)', async function () { + let publishedPost = {status: 'published', ...mentionsPost}; + await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + await sleep(10); + + assert.equal(mentionMock.isDone(), true); + assert.equal(endpointMock.isDone(), true); + }); + + it('Edited published post (post.published.edited)', async function () { + let publishedPost = {status: 'published', ...mentionsPost}; + let res = await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + await sleep(10); + + // while not the point of the test, we should have real links/mentions to start with + assert.equal(mentionMock.isDone(), true); + assert.equal(endpointMock.isDone(), true); + + // reset mocks for mention + let mentionMockTwo = nock(mentionUrl.href) + .get('/') + .reply(200, targetHtml, {'content-type': 'text/html'}); + let endpointMockTwo = nock(endpointUrl.href) + .post('/') + .reply(201); + + let postId = res.body.posts[0].id; + let editedPost = { + mobiledoc: markdownToMobiledoc(mentionHtml + 'More content'), + updated_at: res.body.posts[0].updated_at + }; + + await agent.put(`posts/${postId}/`) + .body({posts: [editedPost]}) + .expectStatus(200); + + await sleep(10); + + assert.equal(mentionMockTwo.isDone(), true); + assert.equal(endpointMockTwo.isDone(), true); + }); + + it('Unpublished post (post.unpublished)', async function () { + let publishedPost = {status: 'published', ...mentionsPost}; + let res = await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + await sleep(10); + + // while not the point of the test, we should have real links/mentions to start with + assert.equal(mentionMock.isDone(), true); + assert.equal(endpointMock.isDone(), true); + + // reset mocks for mention + let mentionMockTwo = nock(mentionUrl.href) + .get('/') + .reply(200, targetHtml, {'content-type': 'text/html'}); + let endpointMockTwo = nock(endpointUrl.href) + .post('/') + .reply(201); + + let postId = res.body.posts[0].id; + // moving back to draft is how we unpublish + let unpublishedPost = { + status: 'draft', + updated_at: res.body.posts[0].updated_at + }; + await agent.put(`posts/${postId}/`) + .body({posts: [unpublishedPost]}) + .expectStatus(200); + + await sleep(10); + + assert.equal(mentionMockTwo.isDone(), true); + assert.equal(endpointMockTwo.isDone(), true); + }); + + it('Sends for links that got removed from a post', async function () { + let publishedPost = {status: 'published', ...mentionsPost}; + let res = await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + await sleep(10); + + // while not the point of the test, we should have real links/mentions to start with + assert.equal(mentionMock.isDone(), true); + assert.equal(endpointMock.isDone(), true); + + // reset mocks for mention + let mentionMockTwo = nock(mentionUrl.href) + .get('/') + .reply(200, targetHtml, {'content-type': 'text/html'}); + let endpointMockTwo = nock(endpointUrl.href) + .post('/') + .reply(201); + + let postId = res.body.posts[0].id; + let editedPost = { + mobiledoc: markdownToMobiledoc(`mentions were removed from this post`), + updated_at: res.body.posts[0].updated_at + }; + await agent.put(`posts/${postId}/`) + .body({posts: [editedPost]}) + .expectStatus(200); + + await sleep(10); + + assert.equal(mentionMockTwo.isDone(), true); + assert.equal(endpointMockTwo.isDone(), true); + }); + + // there's no special handling for this atm, but could be down the road + it('New paid post', async function () { + let publishedPost = {status: 'published', visibility: 'paid', ...mentionsPost}; + await agent + .post('posts/') + .body({posts: [publishedPost]}) + .expectStatus(201); + + await sleep(10); + + assert.equal(mentionMock.isDone(), true); + assert.equal(endpointMock.isDone(), true); + }); + }); + }); +}); \ No newline at end of file