mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-19 00:11:49 +03:00
b5fc527f8d
refs https://github.com/TryGhost/Product/issues/3957 This changes how we fetch recommendations: - Relations can be included in one query instead of extra queries - Sorting is now possible by click or subscriber counts
682 lines
28 KiB
JavaScript
682 lines
28 KiB
JavaScript
const {agentProvider, fixtureManager, mockManager, matchers} = require('../../utils/e2e-framework');
|
|
const {anyObjectId, anyErrorId, anyISODateTime, anyContentVersion, anyLocationFor, anyEtag} = matchers;
|
|
const assert = require('assert/strict');
|
|
const recommendationsService = require('../../../core/server/services/recommendations');
|
|
const {Recommendation, ClickEvent, SubscribeEvent} = require('@tryghost/recommendations');
|
|
|
|
async function addDummyRecommendation(i = 0) {
|
|
const recommendation = Recommendation.create({
|
|
title: `Recommendation ${i}`,
|
|
reason: `Reason ${i}`,
|
|
url: new URL(`https://recommendation${i}.com`),
|
|
favicon: new URL(`https://recommendation${i}.com/favicon.ico`),
|
|
featuredImage: new URL(`https://recommendation${i}.com/featured.jpg`),
|
|
excerpt: 'Test excerpt',
|
|
oneClickSubscribe: true,
|
|
createdAt: new Date(i * 5000) // Reliable ordering
|
|
});
|
|
|
|
await recommendationsService.repository.save(recommendation);
|
|
return recommendation.id;
|
|
}
|
|
|
|
async function addDummyRecommendations(amount = 15) {
|
|
// Add 15 recommendations using the repository
|
|
for (let i = 0; i < amount; i++) {
|
|
await addDummyRecommendation(i);
|
|
}
|
|
}
|
|
|
|
async function addClicksAndSubscribers({memberId}) {
|
|
const recommendations = await recommendationsService.repository.getAll({order: [{field: 'createdAt', direction: 'desc'}]});
|
|
|
|
// Create 2 clicks for 1st
|
|
for (let i = 0; i < 2; i++) {
|
|
const clickEvent = ClickEvent.create({
|
|
recommendationId: recommendations[0].id
|
|
});
|
|
|
|
await recommendationsService.clickEventRepository.save(clickEvent);
|
|
}
|
|
|
|
// Create 3 clicks for 2nd
|
|
for (let i = 0; i < 3; i++) {
|
|
const clickEvent = ClickEvent.create({
|
|
recommendationId: recommendations[1].id
|
|
});
|
|
|
|
await recommendationsService.clickEventRepository.save(clickEvent);
|
|
}
|
|
|
|
// Create 3 subscribers for 1st
|
|
for (let i = 0; i < 3; i++) {
|
|
const subscribeEvent = SubscribeEvent.create({
|
|
recommendationId: recommendations[0].id,
|
|
memberId
|
|
});
|
|
|
|
await recommendationsService.subscribeEventRepository.save(subscribeEvent);
|
|
}
|
|
|
|
// Create 2 subscribers for 3rd
|
|
for (let i = 0; i < 2; i++) {
|
|
const subscribeEvent = SubscribeEvent.create({
|
|
recommendationId: recommendations[2].id,
|
|
memberId
|
|
});
|
|
|
|
await recommendationsService.subscribeEventRepository.save(subscribeEvent);
|
|
}
|
|
}
|
|
|
|
describe('Recommendations Admin API', function () {
|
|
let agent, memberId;
|
|
|
|
before(async function () {
|
|
agent = await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init('posts', 'members');
|
|
await agent.loginAsOwner();
|
|
|
|
memberId = fixtureManager.get('members', 0).id;
|
|
});
|
|
|
|
afterEach(async function () {
|
|
for (const recommendation of (await recommendationsService.repository.getAll())) {
|
|
recommendation.delete();
|
|
await recommendationsService.repository.save(recommendation);
|
|
}
|
|
mockManager.restore();
|
|
});
|
|
|
|
describe('browse', function () {
|
|
it('Can browse', async function () {
|
|
await addDummyRecommendation();
|
|
|
|
await agent.get('recommendations/')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Can request pages', async function () {
|
|
// Add 15 recommendations using the repository
|
|
await addDummyRecommendations(15);
|
|
|
|
const {body: page1} = await agent.get('recommendations/?page=1&limit=10')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(10).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.meta.pagination.page, 1);
|
|
assert.equal(page1.meta.pagination.limit, 10);
|
|
assert.equal(page1.meta.pagination.pages, 2);
|
|
assert.equal(page1.meta.pagination.next, 2);
|
|
assert.equal(page1.meta.pagination.prev, null);
|
|
assert.equal(page1.meta.pagination.total, 15);
|
|
|
|
const {body: page2} = await agent.get('recommendations/?page=2&limit=10')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page2.meta.pagination.page, 2);
|
|
assert.equal(page2.meta.pagination.limit, 10);
|
|
assert.equal(page2.meta.pagination.pages, 2);
|
|
assert.equal(page2.meta.pagination.next, null);
|
|
assert.equal(page2.meta.pagination.prev, 1);
|
|
assert.equal(page2.meta.pagination.total, 15);
|
|
});
|
|
|
|
it('Uses default limit of 5', async function () {
|
|
await addDummyRecommendations(6);
|
|
const {body: page1} = await agent.get('recommendations/')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
});
|
|
|
|
assert.equal(page1.meta.pagination.limit, 5);
|
|
});
|
|
|
|
it('Can include click and subscribe counts', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 2);
|
|
assert.equal(page1.recommendations[1].count.clicks, 3);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 2);
|
|
});
|
|
|
|
it('Can include only clicks', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 2);
|
|
assert.equal(page1.recommendations[1].count.clicks, 3);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, undefined);
|
|
assert.equal(page1.recommendations[1].count.subscribers, undefined);
|
|
assert.equal(page1.recommendations[2].count.subscribers, undefined);
|
|
});
|
|
|
|
it('Can include only subscribers', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, undefined);
|
|
assert.equal(page1.recommendations[1].count.clicks, undefined);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 2);
|
|
});
|
|
|
|
it('Can include click and subscribe counts and order by clicks+subscribe count', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers&order=' + encodeURIComponent('count.clicks desc, count.subscribers asc'))
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 3);
|
|
assert.equal(page1.recommendations[1].count.clicks, 2);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 0);
|
|
});
|
|
|
|
it('Can order by click and subscribe counts and they will be included by default', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?order=' + encodeURIComponent('count.clicks desc, count.subscribers asc'))
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 3);
|
|
assert.equal(page1.recommendations[1].count.clicks, 2);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 0);
|
|
});
|
|
|
|
it('Can fetch recommendations with relations when there are no recommendations', async function () {
|
|
const recommendations = await recommendationsService.repository.getCount();
|
|
assert.equal(recommendations, 0, 'This test expects there to be no recommendations');
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
|
|
assert.equal(page1.recommendations.length, 0);
|
|
});
|
|
|
|
it('can fetch recommendations filtered by an exact title', async function () {
|
|
await addDummyRecommendations(5);
|
|
|
|
const {body} = await agent.get(`recommendations/?filter=title:'Recommendation 1'`)
|
|
.expectStatus(200);
|
|
|
|
assert.equal(body.recommendations.length, 1);
|
|
assert.equal(body.recommendations[0].title, 'Recommendation 1');
|
|
});
|
|
|
|
it('can fetch recommendations filtered by a partial URL', async function () {
|
|
await addDummyRecommendations(5);
|
|
|
|
const {body} = await agent.get(`recommendations/?filter=url:~'recommendation1.com'`)
|
|
.expectStatus(200);
|
|
|
|
assert.equal(body.recommendations.length, 1);
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation1.com/');
|
|
});
|
|
});
|
|
|
|
describe('read', function () {
|
|
it('can get a recommendation by ID', async function () {
|
|
const id = await addDummyRecommendation(1);
|
|
const {body} = await agent.get(`recommendations/${id}/`)
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check data
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Recommendation 1');
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation1.com/');
|
|
assert.equal(body.recommendations[0].reason, 'Reason 1');
|
|
assert.equal(body.recommendations[0].excerpt, 'Test excerpt');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://recommendation1.com/featured.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://recommendation1.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('returns an empty array when the recommendation is not found', async function () {
|
|
const id = 'i-dont-exist';
|
|
const {body} = await agent.get(`recommendations/${id}/`)
|
|
.expectStatus(422)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
|
|
assert.equal(body.errors[0].type, 'ValidationError');
|
|
assert.equal(body.errors[0].message, 'Validation error, cannot read recommendation.');
|
|
});
|
|
});
|
|
|
|
describe('edit', function () {
|
|
it('Can edit recommendation', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Cat Pictures',
|
|
url: 'https://dogpictures.com',
|
|
reason: 'Because cats are cute',
|
|
excerpt: 'Cats are cute',
|
|
featured_image: 'https://catpictures.com/cat.jpg',
|
|
favicon: 'https://catpictures.com/favicon.ico',
|
|
one_click_subscribe: false
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Cat Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].reason, 'Because cats are cute');
|
|
assert.equal(body.recommendations[0].excerpt, 'Cats are cute');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://catpictures.com/cat.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://catpictures.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, false);
|
|
});
|
|
|
|
it('Can edit recommendation and set nullable fields to null', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
reason: null,
|
|
excerpt: null,
|
|
featured_image: null,
|
|
favicon: null
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].reason, null);
|
|
assert.equal(body.recommendations[0].excerpt, null);
|
|
assert.equal(body.recommendations[0].featured_image, null);
|
|
assert.equal(body.recommendations[0].favicon, null);
|
|
});
|
|
|
|
it('Can edit some fields of a recommendation without changing others', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Changed'
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Changed');
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation0.com/');
|
|
assert.equal(body.recommendations[0].reason, 'Reason 0');
|
|
assert.equal(body.recommendations[0].excerpt, 'Test excerpt');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://recommendation0.com/featured.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://recommendation0.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('Cannot use invalid protocols when editing', async function () {
|
|
const id = await addDummyRecommendation();
|
|
|
|
await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Cat Pictures',
|
|
url: 'https://dogpictures.com',
|
|
reason: 'Because cats are cute',
|
|
excerpt: 'Cats are cute',
|
|
featured_image: 'ftp://dogpictures.com/dog.jpg',
|
|
favicon: 'ftp://dogpictures.com/favicon.ico',
|
|
one_click_subscribe: false
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('add', function () {
|
|
it('Can add a minimal recommendation', async function () {
|
|
const {body} = await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Dog Pictures',
|
|
url: 'https://dogpictures.com'
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].title, 'Dog Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].reason, null);
|
|
assert.equal(body.recommendations[0].excerpt, null);
|
|
assert.equal(body.recommendations[0].featured_image, null);
|
|
assert.equal(body.recommendations[0].favicon, null);
|
|
assert.equal(body.recommendations[0].one_click_subscribe, false);
|
|
});
|
|
|
|
it('Can add a full recommendation', async function () {
|
|
const {body} = await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Dog Pictures',
|
|
url: 'https://dogpictures.com',
|
|
reason: 'Because dogs are cute',
|
|
excerpt: 'Dogs are cute',
|
|
featured_image: 'https://dogpictures.com/dog.jpg',
|
|
favicon: 'https://dogpictures.com/favicon.ico',
|
|
one_click_subscribe: true
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].title, 'Dog Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].reason, 'Because dogs are cute');
|
|
assert.equal(body.recommendations[0].excerpt, 'Dogs are cute');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://dogpictures.com/dog.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://dogpictures.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('Can add a recommendation with the same hostname but different paths', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with a different path',
|
|
url: 'https://recommendation3.com/path-1'
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Cannot add the same recommendation URL twice (exact URL match)', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with the exact same URL',
|
|
url: 'https://recommendation3.com'
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Cannot add the same recommendation twice (partial URL match)', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with the same hostname and pathname, but with different protocol, www, query params and hash fragement',
|
|
url: 'http://www.recommendation3.com/?query=1#hash'
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('delete', function () {
|
|
it('Can delete recommendation', async function () {
|
|
const id = await addDummyRecommendation();
|
|
await agent.delete(`recommendations/${id}/`)
|
|
.expectStatus(204)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
});
|
|
});
|
|
});
|