Skip to content

Commit

Permalink
test(network events): updated network tests
Browse files Browse the repository at this point in the history
Improved network detection of errors as well error handling when certain network status happens.
  • Loading branch information
Erick Rodriguez committed Jan 19, 2022
1 parent 369b9c2 commit c424dff
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 23 deletions.
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -50,6 +50,7 @@
"@types/nock": "^11.1.0",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.33.0",
"axios-mock-adapter": "^1.20.0",
"clean-publish": "^2.2.0",
"commitizen": "^4.2.4",
"coveralls": "^3.1.1",
Expand Down
18 changes: 11 additions & 7 deletions src/iTunesSearch/index.ts
@@ -1,4 +1,4 @@
import axios from 'axios';
import axios from 'axios';

import { ReturnType } from "../typings/types";
import {
Expand Down Expand Up @@ -47,25 +47,29 @@ export class iTunesSearch implements IiTunesSearch {
searchAll = async (term: string, options?: ISearchAllOptions): Promise<ReturnType> =>
await this.performSearch(term, { ...options }, "searchAll")

private performSearch = async (term: string, options: ISearchAllOptions, validate:string): Promise<ReturnType> => {
private performSearch = async (term: string, options: ISearchAllOptions, validate:string): Promise<ReturnType | any> => {
const searchObject: any = this.validate(term, options, validate);
// eslint-disable-next-line no-useless-catch
try {
const iTunesFetch = axios.create({
baseURL: 'https://itunes.apple.com',
timeout: searchObject?.timeout || 2000
timeout: searchObject.timeout
});
if (searchObject.timeout) delete searchObject.timeout;

const querystring = new URLSearchParams(searchObject);
const searchQueryStr = `/search?term=${encodeURI(term)}&${querystring.toString()}`;
const { data } = await iTunesFetch.get(searchQueryStr);
return data;
} catch (e) {
throw e;
} catch (e: any) {
if (axios.isAxiosError(e)) {
if (e.code) {
throw new Error(`Error: ${e.code}. Reference: https://www.ibm.com/docs/en/zos/2.2.0?topic=codes-sockets-return-errnos`);
} else {
throw new Error('Network Disconnected');
}
}
}
}

private validate(term:string, options:ISearchAllOptions, validate:string): ReturnType {

//cleaning up nulls
Expand Down
85 changes: 85 additions & 0 deletions tests/source/errorHandling.test.ts
@@ -0,0 +1,85 @@
import nock from 'nock';

import { searchAll } from '../../src/index';
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
const scope = nock('https://itunes.apple.com');

const requestURL = '/search?term=Hikaru%20Utada&limit=1&country=US&language=en&attribute=allArtistTerm&entity=allArtist';

describe('Handling of network Errors', () => {
beforeEach(() => {
nock.disableNetConnect();


});

afterEach(() => {
nock.enableNetConnect();

});

afterAll(async () => {
await new Promise((resolve) => setTimeout(() => resolve(null), 3000)); // avoid jest open handle error
});

it('should return an exception when the server times out (NOT THE QUERY)', async () => {
jest.setTimeout(2000);
const mock = new MockAdapter(axios);
scope.get(requestURL)
.delay(1000)
.reply(200, {});

mock.onGet(requestURL).networkError();

await expect(searchAll('Hikaru Utada', {
limit: 1,
attribute: "allArtistTerm",
entity: "allArtist",
}))
.rejects
.toThrow(
'Network Disconnected'
);
mock.restore();
});

it('should return an exception when the connection is timed out and is Aborted', async () => {
jest.setTimeout(2000);
const mock = new MockAdapter(axios);
scope.get(requestURL)
.delay(1000)
.replyWithError({code: 'ECONNABORTED'});

mock.onGet(requestURL).timeout();

await expect(searchAll('Hikaru Utada', {
limit: 1,
attribute: "allArtistTerm",
entity: "allArtist",
}))
.rejects
.toThrow(
'Error: ECONNABORTED. Reference: https://www.ibm.com/docs/en/zos/2.2.0?topic=codes-sockets-return-errnos'
);
mock.restore();
});

it('should return an exception when the query times out after 500 milliseconds', async () => {
jest.setTimeout(2000);
scope.get(requestURL)
.delayConnection(1000)
.replyWithError('error');

await expect(searchAll('Hikaru Utada', {
limit: 1,
attribute: "allArtistTerm",
entity: "allArtist",
timeout: 500
}))
.rejects
.toThrow(
'Error: ECONNABORTED. Reference: https://www.ibm.com/docs/en/zos/2.2.0?topic=codes-sockets-return-errnos'
);
});
})
7 changes: 5 additions & 2 deletions tests/source/searchAlbum.test.ts
Expand Up @@ -21,10 +21,13 @@ describe('Search Album', () => {
limit: 3,
country: 'US',
language: 'en',
entity:'album',
entity: 'album'
})
.reply(200, mockData);
const result = await searchAlbum('Nemesis', {limit:3});
const result = await searchAlbum('Nemesis', {
limit: 3,
timeout: 2000
});
expect(result.resultCount).toBe(3);
expect(result.results).toHaveLength(3);
});
Expand Down
14 changes: 11 additions & 3 deletions tests/source/searchAll.test.ts
@@ -1,7 +1,7 @@
import nock from 'nock';

import { searchAll } from '../../src/index';
import mockData from '../mock-answers/searchAll.json';
import mockData from '../mock-answers/searchAll.json';
const scope = nock('https://itunes.apple.com');

describe('Search All', () => {
Expand All @@ -17,7 +17,12 @@ describe('Search All', () => {
it('should return results for an serch with a keyterm "Hikaru Utada", entity "allArtist" and attribute "allArtistTerm"', async () => {
scope.get('/search?term=Hikaru%20Utada&limit=1&country=US&language=en&attribute=allArtistTerm&entity=allArtist')
.reply(200, mockData);
const result = await searchAll('Hikaru Utada', {limit:1, attribute:"allArtistTerm", entity:"allArtist"});
const result = await searchAll('Hikaru Utada', {
limit: 1,
attribute: "allArtistTerm",
entity: "allArtist",
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
expect(result.results[0]).toEqual({
Expand Down Expand Up @@ -49,10 +54,13 @@ describe('Search All', () => {
expect(result.resultCount).toBe(0);
expect(result.results).toHaveLength(0);
});



describe("it validates if you're passing valid optional parameters", () => {
it("should throw exception when term is not a string, or empty string", async () => {
const exceptionMessage = 'A "term" is a string required on any search. "term" cannot have empty spaces as well.';
await expect(searchAll('', {limit:1, attribute:"allArtistTerm", entity:"allArtist"}))
await expect(searchAll('', { limit: 1, attribute: "allArtistTerm", entity: "allArtist" }))
.rejects
.toHaveProperty(
'message', exceptionMessage
Expand Down
5 changes: 4 additions & 1 deletion tests/source/searchApp.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search App', () => {
it("should return results for an App called 'Captain Tsubasa Dream Team'", async () => {
scope.get('/search?term=Captain%20Tsubasa%20Dream%20Team&limit=1&country=US&language=en&entity=software')
.reply(200, mockData);
const result = await searchApp('Captain Tsubasa Dream Team', {limit:1});
const result = await searchApp('Captain Tsubasa Dream Team', {
limit: 1,
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand Down
5 changes: 4 additions & 1 deletion tests/source/searchArtist.test.ts
Expand Up @@ -19,7 +19,10 @@ describe('Search Artist', () => {
it("should return results for an artist called 'Hikaru Utada'", async () => {
scope.get('/search?term=Hikaru%20Utada&limit=1&country=US&language=en&attribute=allArtistTerm&entity=allArtist')
.reply(200, mockData);
const result = await searchArtist('Hikaru Utada');
const result = await searchArtist('Hikaru Utada', {
limit: 1,
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand Down
10 changes: 8 additions & 2 deletions tests/source/searchAudiobook.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Audiobook', () => {
it("should return results for an Audiobook called 'moby dick'", async () => {
scope.get('/search?term=moby%20dick&limit=1&country=US&language=en&entity=audiobook')
.reply(200, mockData);
const result = await searchAudiobook('moby dick');
const result = await searchAudiobook('moby dick', {
limit: 1,
timeout: 2000
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand All @@ -36,7 +39,10 @@ describe('Search Audiobook', () => {
describe("it validates if you're passing valid optional parameters", () => {
it("should throw exception when term is not a string, or empty string", async () => {
const exceptionMessage = 'A "term" is a string required on any search. "term" cannot have empty spaces as well.';
await expect(searchAudiobook('', { limit: 3 }))
await expect(searchAudiobook('', {
limit: 3,
timeout: 2000,
}))
.rejects
.toHaveProperty(
'message', exceptionMessage
Expand Down
10 changes: 8 additions & 2 deletions tests/source/searchBook.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Book', () => {
it("should return results for an ebook called 'moby dick'", async () => {
scope.get('/search?term=moby%20dick&limit=1&country=US&language=en&entity=ebook')
.reply(200, mockData);
const result = await searchBook('moby dick');
const result = await searchBook('moby dick', {
limit: 1,
timeout: 2000
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand All @@ -36,7 +39,10 @@ describe('Search Book', () => {
describe("it validates if you're passing valid optional parameters", () => {
it("should throw exception when term is not a string, or empty string", async () => {
const exceptionMessage = 'A "term" is a string required on any search. "term" cannot have empty spaces as well.';
await expect(searchBook('', { limit: 3 }))
await expect(searchBook('', {
limit: 3,
timeout: 2000,
}))
.rejects
.toHaveProperty(
'message', exceptionMessage
Expand Down
10 changes: 8 additions & 2 deletions tests/source/searchMovie.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Movie', () => {
it("should return results for an movie called 'Deadpool'", async () => {
scope.get('/search?term=Deadpool&limit=1&country=US&language=en&entity=movie')
.reply(200, mockData);
const result = await searchMovie('Deadpool');
const result = await searchMovie('Deadpool', {
limit: 1,
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand All @@ -36,7 +39,10 @@ describe('Search Movie', () => {
describe("it validates if you're passing valid optional parameters", () => {
it("should throw exception when term is not a string, or empty string", async () => {
const exceptionMessage = 'A "term" is a string required on any search. "term" cannot have empty spaces as well.';
await expect(searchMovie('', { limit: 3 }))
await expect(searchMovie('', {
limit: 3,
timeout: 2000,
}))
.rejects
.toHaveProperty(
'message', exceptionMessage
Expand Down
5 changes: 4 additions & 1 deletion tests/source/searchMusicVideo.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Music Video', () => {
it("should return results for a music video called 'u cant touch this'", async () => {
scope.get('/search?term=u%20cant%20touch%20this&limit=1&country=US&language=en&media=musicVideo')
.reply(200, mockData);
const result = await searchMusicVideo('u cant touch this');
const result = await searchMusicVideo('u cant touch this', {
limit: 1,
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand Down
5 changes: 4 additions & 1 deletion tests/source/searchPodcast.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Podcast', () => {
it("should return results for a podcast with the keyword 'cbs news'", async () => {
scope.get('/search?term=cbs%20news&limit=1&country=US&language=en&entity=podcast')
.reply(200, mockData);
const result = await searchPodcast('cbs news');
const result = await searchPodcast('cbs news', {
limit: 1,
timeout: 2000,
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand Down
5 changes: 4 additions & 1 deletion tests/source/searchSong.test.ts
Expand Up @@ -18,7 +18,10 @@ describe('Search Song', () => {
it("should return results for a song called 'Home by the sea'", async () => {
scope.get('/search?term=Home%20by%20the%20sea&limit=1&country=US&language=en&media=music')
.reply(200, mockData);
const result = await searchSong('Home by the sea');
const result = await searchSong('Home by the sea', {
limit: 1,
timeout: 2000
});
expect(result.resultCount).toBe(1);
expect(result.results).toHaveLength(1);
});
Expand Down

0 comments on commit c424dff

Please sign in to comment.