diff --git a/.eslintrc.js b/.eslintrc.js index d1d2cbe..8da6be5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,7 +30,7 @@ module.exports = { 'block-scoped-var': 'warn', 'class-methods-use-this': 'warn', 'curly': ['warn', 'all'], - 'dot-location': ['warn', 'object'], + 'dot-location': ['warn', 'property'], 'dot-notation': ['error', { 'allowKeywords': true }], diff --git a/server/api.js b/server/api.js index d66e5a0..5a98699 100644 --- a/server/api.js +++ b/server/api.js @@ -73,6 +73,8 @@ const shortenUrl = url => { }), { // eslint-disable-next-line camelcase max_tries: 10, + // We want to retry the request instantly if it fails. + interval: 0, timeout: 5000 }).then(data => data.id).catch(error => { throw new ServerError('Error shortening a URL', error) @@ -160,8 +162,6 @@ const fetchArticles = source => { module.exports = exports = { BAD_SOURCE, - NEWS_API_BASE_URL, - URL_SHORTENER_BASE_URL, shortenUrl, fetchSources, fetchArticles diff --git a/test/api_test.js b/test/api_test.js index 8e918c4..fc57256 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -10,21 +10,113 @@ const nock = require('nock') chai.use(chaiAsPromised) chai.should() -process.env.NEWS_API_KEY = 'news_api_key' -process.env.URL_SHORTENER_API_KEY = 'key' +const NEWS_API_KEY = 'key1' +const URL_SHORTENER_API_KEY = 'key2' + +process.env.NEWS_API_KEY = NEWS_API_KEY +process.env.URL_SHORTENER_API_KEY = URL_SHORTENER_API_KEY const api = require('../server/api') -describe('shortenUrl()', () => { - before(() => { - nock(api.URL_SHORTENER_BASE_URL).post().reply(200, { - id: 'test' +describe('api.js', function() { + this.timeout(10000) + + describe('shortenUrl()', () => { + before(() => { + nock('https://www.googleapis.com') + .post('/urlshortener/v1/url', { longUrl: 'test1' }) + .query({ key: URL_SHORTENER_API_KEY }) + .reply(200, { id: 'test_result_id' }) + .post('/urlshortener/v1/url', { longUrl: 'test2' }) + .query({ key: URL_SHORTENER_API_KEY }) + .reply(400) + .post('/urlshortener/v1/url', { longUrl: 'test3' }) + .query({ key: URL_SHORTENER_API_KEY }) + .thrice() + .reply(400) + .post('/urlshortener/v1/url', { longUrl: 'test3' }) + .query({ key: URL_SHORTENER_API_KEY }) + .reply(200, { id: 'test_result_id' }) + }) + + it('should work correctly when a valid result is returned', () => { + return api.shortenUrl('test1').should.become('test_result_id') + }) + + it('should return a ServerError if all requests fail', () => { + return api.shortenUrl('test2').should.be.rejected }) + + it('should retry correctly even after few failures', () => { + return api.shortenUrl('test3').should.become('test_result_id') + }) + + after(nock.cleanAll) }) - it('should work', () => { - return api.shortenUrl('asdf').should.eventually.equal('no') + describe('fetchSources()', () => { + before(() => { + nock('https://newsapi.org') + .get('/v1/sources') + .query({ test: 'test1' }) + .reply(200, { sources: 'test_result_source' }) + .get('/v1/sources') + .query({ test: 'test2' }) + .reply(400) + }) + + it('should work correctly when a valid result is returned', () => { + return api.fetchSources({ + test: 'test1' + }).should.become('test_result_source') + }) + + it('should return a ServerError if the request failed', () => { + return api.fetchSources({ + test: 'test2' + }).should.be.rejected + }) + + after(nock.cleanAll) }) - after(() => nock.restore()) + describe('fetchArticles()', () => { + before(() => { + nock('https://www.googleapis.com') + .post('/urlshortener/v1/url', { longUrl: 'test1' }) + .query({ key: URL_SHORTENER_API_KEY }) + .times(10) + .reply(200, { id: 'test_result_id' }) + + nock('https://newsapi.org') + .get('/v1/articles') + .query({ source: 'test1', apiKey: NEWS_API_KEY }) + .reply(200, { + articles: [{ + url: 'test1', publishedAt: 100 + }, { + url: 'test1', publishedAt: 200 + }] + }) + .get('/v1/articles') + .query({ source: 'test2', apiKey: NEWS_API_KEY }) + .reply(400, { + error: 'test_error' + }) + }) + + it('should work correctly when a valid result is returned', () => { + return api.fetchArticles('test1').should.eventually.satisfy(values => { + return values.every(value => { + return value.url === 'test_result_id' && value.publishedAt.isValid() + }) && values.length === 2 + }) + }) + + it('should return a ServerError if the request failed', () => { + return api.fetchArticles('test2').should.be.rejected + }) + + after(nock.cleanAll) + }) })