const MentionDiscoveryService = require('../lib/MentionDiscoveryService');
const sinon = require('sinon');
// non-standard to use externalRequest here, but this is required for the overrides in the library, which we want to test for security reasons in combination with the package
const externalRequest = require('../../core/core/server/lib/request-external.js');
const dnsPromises = require('dns').promises;
const assert = require('assert');
const nock = require('nock');
describe('MentionDiscoveryService', function () {
const service = new MentionDiscoveryService({externalRequest});
beforeEach(function () {
nock.disableNetConnect();
// 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'});
});
});
afterEach(function () {
sinon.restore();
nock.cleanAll();
});
after(function () {
nock.cleanAll();
nock.enableNetConnect();
});
it('Returns null from a bad URL', async function () {
const url = new URL('http://www.notarealsite.com/');
nock(url.href)
.get('/')
.reply(404);
let endpoint = await service.getEndpoint(url);
assert.equal(endpoint, null);
});
it('Follows redirects', async function () {
let url = new URL('http://redirector.io/');
let nextUrl = new URL('http://testpage.com/');
nock(url.href)
.intercept('/', 'HEAD')
.reply(301, undefined, {location: nextUrl.href})
.get('/')
.reply(200, 'Very cool site', {'content-type': 'text/html'});
let endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
});
describe('Can parse headers', function () {
it('Returns null for a valid non-html site', async function () {
const url = new URL('http://www.veryrealsite.com');
nock(url.href)
.get('/')
.reply(200, {}, {'content-type': 'application/json'});
const endpoint = await service.getEndpoint(url);
assert.equal(endpoint, null);
});
it('Returns an endpoint from a site with a webmentions Link in the header', async function () {
const url = new URL('http://testpage.com/');
nock(url.href)
.get('/')
.reply(200, {}, {Link: '; rel="webmention"'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint, 'http://webmentions.endpoint.io/');
});
it('Returns null with Links in the header that are not for webmentions', async function () {
const url = new URL('http://testpage.com/');
nock(url.href)
.get('/')
.reply(200, {}, {Link: '; rel="preconnect"'});
const endpoint = await service.getEndpoint(url);
assert.equal(endpoint, null);
});
it('Returns with multiple Links in the header, one of which is for webmentions', async function () {
const url = new URL('http://testpage.com/');
nock(url.href)
.get('/')
.reply(200, {}, {Link: '; rel="preconnect",; rel="webmention"'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint, 'http://webmentions.endpoint.io/');
});
});
describe('Can parse html', function () {
it('Returns endpoint for valid html site with tag in body', async function () {
const url = new URL('http://testpage.com/');
nock(url.href)
.get('/')
.reply(200, '', {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint, 'http://webmentions.endpoint.io/');
});
it('Returns endpoint for valid html site with tag in body', async function () {
const url = new URL('http://testpage.com/');
nock(url.href)
.get('/')
.reply(200, 'webmention', {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint, 'http://valid.site.org/');
});
it('Returns first endpoint for valid html site with multiple tags in body', async function () {
const url = new URL('http://testpage.com/');
const html = `
kewl link 1kewl link 2kewl link 3kewl link 4
`;
nock(url.href)
.get('/')
.reply(200, html, {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint.href, 'http://first.webmention.endpoint/');
});
it('Returns first endpoint for valid html site with multiple tags in the header', async function () {
const url = new URL('http://testpage.com/');
const html = `
`;
nock(url.href)
.get('/')
.reply(200, html, {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint.href, 'http://first.webmention.endpoint/');
});
it('Ignores link without href', async function () {
const url = new URL('http://testpage.com/');
const html = `
`;
nock(url.href)
.get('/')
.reply(200, html, {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert.equal(endpoint, null);
});
it('Returns first endpoint for valid html site with multiple and tags', async function () {
// note - link tags are in the header and should come first
const url = new URL('http://testpage.com/');
const html = `
kewl link 1kewl link 2
`;
nock(url.href)
.get('/')
.reply(200, html, {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert(endpoint instanceof URL);
assert.equal(endpoint.href, 'http://first.link.endpoint/');
});
it('Returns null for a valid html site with no endpoint', async function () {
const url = new URL('http://www.veryrealsite.com');
nock(url.href)
.get('/')
.reply(200, {}, {'content-type': 'text/html'});
const endpoint = await service.getEndpoint(url);
assert.equal(endpoint, null);
});
});
});