import assert from 'assert/strict'; import sinon from 'sinon'; import {IncomingRecommendationEmailRenderer, IncomingRecommendationService, RecommendationService} from '../src'; describe('IncomingRecommendationService', function () { let service: IncomingRecommendationService; let refreshMentions: sinon.SinonStub; let clock: sinon.SinonFakeTimers; let send: sinon.SinonStub; let readRecommendationByUrl: sinon.SinonStub; beforeEach(function () { refreshMentions = sinon.stub().resolves(); send = sinon.stub().resolves(); readRecommendationByUrl = sinon.stub().resolves(null); service = new IncomingRecommendationService({ recommendationService: { readRecommendationByUrl } as any as RecommendationService, mentionsApi: { refreshMentions, listMentions: () => Promise.resolve({data: []}) }, emailService: { send }, emailRenderer: { renderSubject: () => Promise.resolve(''), renderHTML: () => Promise.resolve(''), renderText: () => Promise.resolve('') } as any as IncomingRecommendationEmailRenderer, getEmailRecipients: () => Promise.resolve([ { email: 'example@example.com' } ]) }); clock = sinon.useFakeTimers(); }); afterEach(function () { sinon.restore(); clock.restore(); }); describe('init', function () { it('should update incoming recommendations on boot', async function () { // Sandbox time const saved = process.env.NODE_ENV; try { process.env.NODE_ENV = 'development'; await service.init(); clock.tick(1000 * 60 * 60 * 24); assert(refreshMentions.calledOnce); } finally { process.env.NODE_ENV = saved; } }); it('ignores errors when update incoming recommendations on boot', async function () { // Sandbox time const saved = process.env.NODE_ENV; try { process.env.NODE_ENV = 'development'; refreshMentions.rejects(new Error('test')); await service.init(); clock.tick(1000 * 60 * 60 * 24); assert(refreshMentions.calledOnce); } finally { process.env.NODE_ENV = saved; } }); }); describe('sendRecommendationEmail', function () { it('should send email', async function () { await service.sendRecommendationEmail({ id: 'test', source: new URL('https://example.com'), sourceTitle: 'Example', sourceSiteTitle: 'Example', sourceAuthor: 'Example', sourceExcerpt: 'Example', sourceFavicon: new URL('https://example.com/favicon.ico'), sourceFeaturedImage: new URL('https://example.com/featured.png') }); assert(send.calledOnce); }); it('ignores when mention not convertable to incoming recommendation', async function () { readRecommendationByUrl.rejects(new Error('test')); await service.sendRecommendationEmail({ id: 'test', source: new URL('https://example.com'), sourceTitle: 'Example', sourceSiteTitle: 'Example', sourceAuthor: 'Example', sourceExcerpt: 'Example', sourceFavicon: new URL('https://example.com/favicon.ico'), sourceFeaturedImage: new URL('https://example.com/featured.png') }); assert(!send.calledOnce); }); }); describe('listIncomingRecommendations', function () { beforeEach(function () { refreshMentions = sinon.stub().resolves(); send = sinon.stub().resolves(); readRecommendationByUrl = sinon.stub().resolves(null); service = new IncomingRecommendationService({ recommendationService: { readRecommendationByUrl } as any as RecommendationService, mentionsApi: { refreshMentions, listMentions: () => Promise.resolve({data: [ { id: 'Incoming recommendation', source: new URL('https://incoming-rec.com/.well-known/recommendations.json'), sourceTitle: 'Incoming recommendation title', sourceSiteTitle: null, sourceAuthor: null, sourceExcerpt: 'Incoming recommendation excerpt', sourceFavicon: new URL('https://incoming-rec.com/favicon.ico'), sourceFeaturedImage: new URL('https://incoming-rec.com/image.png') } ], meta: { pagination: { page: 1, limit: 5, pages: 1, total: 1, next: null, prev: null } }}) }, emailService: { send }, emailRenderer: { renderSubject: () => Promise.resolve(''), renderHTML: () => Promise.resolve(''), renderText: () => Promise.resolve('') } as any as IncomingRecommendationEmailRenderer, getEmailRecipients: () => Promise.resolve([ { email: 'example@example.com' } ]) }); }); it('returns a list of incoming recommendations and pagination', async function () { const list = await service.listIncomingRecommendations({}); assert.equal(list.incomingRecommendations.length, 1); assert.equal(list.incomingRecommendations[0].id, 'Incoming recommendation'); assert.equal(list.incomingRecommendations[0].title, 'Incoming recommendation title'); assert.equal(list.incomingRecommendations[0].excerpt, 'Incoming recommendation excerpt'); assert.equal(list.incomingRecommendations[0].url.toString(), 'https://incoming-rec.com/'); assert.equal(list.incomingRecommendations[0].favicon?.toString(), 'https://incoming-rec.com/favicon.ico'); assert.equal(list.incomingRecommendations[0].featuredImage?.toString(), 'https://incoming-rec.com/image.png'); assert.equal(list.meta?.pagination.page, 1); assert.equal(list.meta?.pagination.limit, 5); assert.equal(list.meta?.pagination.pages, 1); assert.equal(list.meta?.pagination.total, 1); assert.equal(list.meta?.pagination.prev, null); assert.equal(list.meta?.pagination.next, null); }); }); });