const { agentProvider, fixtureManager, mockManager, dbUtils, configUtils } = require('../../utils/e2e-framework'); const models = require('../../../core/server/models'); const assert = require('assert'); const urlUtils = require('../../../core/shared/url-utils'); const nock = require('nock'); const jobsService = require('../../../core/server/services/mentions-jobs'); const DomainEvents = require('@tryghost/domain-events'); async function allSettled() { await jobsService.allSettled(); await DomainEvents.allSettled(); } describe('Webmentions (receiving)', function () { let agent; before(async function () { agent = await agentProvider.getWebmentionsAPIAgent(); await fixtureManager.init('posts'); }); beforeEach(async function () { await allSettled(); mockManager.disableNetwork(); mockManager.mockLabsEnabled('webmentions'); }); afterEach(async function () { await allSettled(); mockManager.restore(); await dbUtils.truncate('brute'); }); it('can receive a webmention', async function () { const targetUrl = new URL('integrations/', urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article/'); const html = ` Test Page `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href, withExtension: true // test payload recorded }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/external-article/'}); assert(mention); assert.equal(mention.get('target'), urlUtils.getSiteUrl() + 'integrations/'); assert.ok(mention.get('resource_id')); assert.equal(mention.get('resource_type'), 'post'); assert.equal(mention.get('source_title'), 'Test Page'); assert.equal(mention.get('source_excerpt'), 'Test description'); assert.equal(mention.get('source_author'), 'John Doe'); assert.equal(mention.get('payload'), JSON.stringify({ withExtension: true })); }); it('will update a mentions source metadata', async function () { const targetUrl = new URL(urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/update-mention-test-1/'); testCreatingTheMention: { const html = ` Test Page `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/update-mention-test-1/'}); assert(mention); assert.equal(mention.get('source_title'), 'Test Page'); assert.equal(mention.get('source_excerpt'), 'Test description'); assert.equal(mention.get('source_author'), 'John Doe'); break testCreatingTheMention; } nock.cleanAll(); testUpdatingTheMention: { const html = ` New Title `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/update-mention-test-1/'}); assert(mention); assert.equal(mention.get('source_title'), 'New Title'); assert.equal(mention.get('source_excerpt'), 'New Description'); assert.equal(mention.get('source_author'), 'big man with a beard'); break testUpdatingTheMention; } }); it('will delete a mention when the target in Ghost was deleted', async function () { const post = await models.Post.findOne({id: fixtureManager.get('posts', 0).id}); const targetUrl = new URL(urlUtils.getSiteUrl() + post.get('slug') + '/'); const sourceUrl = new URL('http://testpage.com/update-mention-test-2/'); const html = ` Test Page `; nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); testCreatingTheMention: { await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/update-mention-test-2/'}); assert(mention); assert.equal(mention.get('resource_id'), post.id); assert.equal(mention.get('source_title'), 'Test Page'); assert.equal(mention.get('source_excerpt'), 'Test description'); assert.equal(mention.get('source_author'), 'John Doe'); break testCreatingTheMention; } // Move post to draft and mark page as 404 await models.Post.edit({status: 'draft'}, {id: post.id}); nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(404); testUpdatingTheMention: { await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/update-mention-test-2/'}); assert(mention); // Check resource id was not cleared assert.equal(mention.get('resource_id'), post.id); // Check deleted assert.equal(mention.get('deleted'), true); break testUpdatingTheMention; } }); it('can receive a webmention to homepage', async function () { const targetUrl = new URL(urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-2/'); const html = ` Test Page `; nock(targetUrl.origin) .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/external-article-2/'}); assert(mention); assert.equal(mention.get('target'), urlUtils.getSiteUrl()); assert.ok(!mention.get('resource_id')); assert.equal(mention.get('resource_type'), null); assert.equal(mention.get('source_title'), 'Test Page'); assert.equal(mention.get('source_excerpt'), 'Test description'); assert.equal(mention.get('source_author'), 'John Doe'); assert.equal(mention.get('payload'), JSON.stringify({})); }); it('can verify a webmention link', async function () { const targetUrl = new URL(urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-2/'); const html = ` Test Pageyour cool website mentioned `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/external-article-2/'}); assert(mention); assert.equal(mention.get('verified'), true); }); it('can verify a webmention link to post', async function () { const targetUrl = new URL('integrations/', urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-3/'); const html = ` Test Pageyour cool website mentioned `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: sourceUrl.href}); assert(mention); assert.equal(mention.get('verified'), true); }); it('can verify a webmention link to post with tracking parameters', async function () { const targetUrl = new URL('integrations/', urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-4/'); const html = ` Test Pageyour cool website mentioned `; nock(targetUrl.origin) .persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: sourceUrl.href}); assert(mention); assert.equal(mention.get('verified'), true); }); it('marks as unverified if url not present on source', async function () { const targetUrl = new URL('html-ipsum', urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-not-present/'); const html = ` Test Pageyour cool website mentioned `; nock(targetUrl.origin).persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: sourceUrl.toString()}); assert(mention); assert.equal(mention.get('verified'), false); }); it('can verify a webmention link', async function () { const targetUrl = new URL(urlUtils.getSiteUrl()); const sourceUrl = new URL('http://testpage.com/external-article-2/'); const html = ` Test Page `; nock(targetUrl.origin).persist() .head(targetUrl.pathname) .reply(200); nock(sourceUrl.origin) .persist() .get(sourceUrl.pathname) .reply(200, html, {'Content-Type': 'text/html'}); await agent.post('/receive') .body({ source: sourceUrl.href, target: targetUrl.href }) .expectStatus(202); await allSettled(); const mention = await models.Mention.findOne({source: 'http://testpage.com/external-article-2/'}); assert(mention); assert.equal(mention.get('verified'), true); }); it('can verify a webmention