diff --git a/__tests__/api/health.test.ts b/__tests__/api/health.test.ts new file mode 100644 index 000000000..a3fc4eb00 --- /dev/null +++ b/__tests__/api/health.test.ts @@ -0,0 +1,30 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from 'axios'; +import healthApi from '../../src/api/health'; + +describe('healthApi', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + it('getHealth should return health data', async () => { + const data = { status: 'pass' }; + mock.onGet('version').reply(200, data); + + const result = await healthApi.getHealth(); + + expect(result).toEqual(data); + }); + + it('getHealth should allow capturing errors in case of network error', async () => { + mock.onGet('version').networkError(); + + await expect(healthApi.getHealth()).rejects.toThrow(); + }); +}); \ No newline at end of file diff --git a/__tests__/api/txMining.test.ts b/__tests__/api/txMining.test.ts new file mode 100644 index 000000000..2b16534bd --- /dev/null +++ b/__tests__/api/txMining.test.ts @@ -0,0 +1,147 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from 'axios'; +import txMiningApi from '../../src/api/txMining'; + +describe('txMiningApi', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('getJobStatus', () => { + it('should send the right request', async () => { + const data = { status: 'done' }; + mock.onGet('job-status').reply(200, data); + + const result = await new Promise((resolve, reject) => { + txMiningApi.getJobStatus('jobId', resolve); + }); + + expect(result).toEqual(data); + + expect(mock.history.get.length).toBe(1); + expect(mock.history.get[0].params).toEqual({ "job-id": 'jobId' }); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onGet('job-status').networkError(); + + await expect(txMiningApi.getJobStatus('jobId', () => {})).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onGet('job-status').reply(500); + + try { + await txMiningApi.getJobStatus('jobId', () => {}); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); + + describe('cancelJob', () => { + it('should send the right request', async () => { + const data = { status: 'cancelled' }; + mock.onPost('cancel-job').reply(200, data); + + const result = await new Promise((resolve, reject) => { + txMiningApi.cancelJob('jobId', resolve); + }); + + expect(result).toEqual(data); + + expect(mock.history.post.length).toBe(1); + expect(mock.history.post[0].data).toEqual(JSON.stringify({ "job-id": 'jobId' })); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onPost('cancel-job').networkError(); + + await expect(txMiningApi.cancelJob('jobId', () => {})).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onPost('cancel-job').reply(500); + + try { + await txMiningApi.cancelJob('jobId', () => {}); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); + + describe('getHealth', () => { + it('should send the right request', async () => { + const data = { status: 'pass' }; + mock.onGet('health').reply(200, data); + + const result = await txMiningApi.getHealth(); + + expect(result).toEqual(data); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onGet('health').networkError(); + + await expect(txMiningApi.getHealth()).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onGet('health').reply(500); + + try { + await txMiningApi.getHealth(); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); + + describe('submitJob', () => { + it('should send the right request', async () => { + const data = { job: 'jobId' }; + mock.onPost('submit-job').reply(200, data); + + const result = await new Promise((resolve, reject) => { + txMiningApi.submitJob('tx', true, true, 10, resolve); + }); + + expect(result).toEqual(data); + + expect(mock.history.post.length).toBe(1); + expect(mock.history.post[0].data).toBe(JSON.stringify({ + 'tx': 'tx', + 'propagate': true, + 'add_parents': true, + 'timeout': 10 + })); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onPost('submit-job').networkError(); + + await expect(txMiningApi.submitJob('tx', true, true, 10, () => {})).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onPost('submit-job').reply(500); + + try { + await txMiningApi.submitJob('tx', true, true, 10, () => {}); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); +}); \ No newline at end of file diff --git a/__tests__/api/version.test.ts b/__tests__/api/version.test.ts new file mode 100644 index 000000000..d11b16651 --- /dev/null +++ b/__tests__/api/version.test.ts @@ -0,0 +1,73 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from 'axios'; +import versionApi from '../../src/api/version'; + +describe('versionApi', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('getVersion', () => { + it('should return version data', async () => { + const data = { version: '1.0.0' }; + mock.onGet('/version').reply(200, data); + + const result = await new Promise((resolve, reject) => { + versionApi.getVersion(resolve); + }); + + expect(result).toEqual(data); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onGet('/version').networkError(); + + await expect(versionApi.getVersion(() => {})).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onGet('/version').reply(500); + + try { + await versionApi.getVersion(() => {}); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); + + describe('asyncGetVersion', () => { + it('should return version data', async () => { + const data = { version: '1.0.0' }; + mock.onGet('/version').reply(200, data); + + const result = await versionApi.asyncGetVersion(); + + expect(result).toEqual(data); + }); + + it('should allow capturing errors in case of network error', async () => { + mock.onGet('/version').networkError(); + + await expect(versionApi.asyncGetVersion()).rejects.toThrow(); + }); + + it('should allow capturing errors in case the server responds with 500', async () => { + mock.onGet('/version').reply(500); + + try { + await versionApi.asyncGetVersion(); + } catch (e) { + expect(e.message).toEqual('Request failed with status code 500'); + expect(e.response.status).toEqual(500); + } + }); + }); +}); \ No newline at end of file diff --git a/src/api/health.js b/src/api/health.js new file mode 100644 index 000000000..53a4110f7 --- /dev/null +++ b/src/api/health.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { createRequestInstance } from './axiosInstance'; + +/** + * Api calls for healthcheck + * + * @namespace ApiHealth + */ + +const healthApi = { + /** + * Get health information of full node running in connected server + * + * @return {Promise} + * @memberof ApiHealth + * @inner + */ + async getHealth() { + return new Promise((resolve, reject) => { + // TODO: We should chage this to get `health` instead of `version` + createRequestInstance(resolve).get(`version`).then((res) => { + resolve(res.data); + }, (err) => { + reject(err); + }); + }); + } +}; + +export default healthApi; diff --git a/src/api/txApi.js b/src/api/txApi.js index 21e48270c..92739d4a3 100644 --- a/src/api/txApi.js +++ b/src/api/txApi.js @@ -71,7 +71,7 @@ const txApi = { return this.getTransactionBase(data, resolve); }, - /* + /** * Call api to get confirmation data of a tx * * @param {string} id Transaction hash in hex @@ -90,7 +90,7 @@ const txApi = { }); }, - /* + /** * Call api to decode a transaction * * @param {string} hex_tx Full transaction in hexadecimal @@ -109,7 +109,7 @@ const txApi = { }); }, - /* + /** * Call api to push a transaction * * @param {string} hex_tx Full transaction in hexadecimal @@ -128,7 +128,7 @@ const txApi = { }); }, - /* + /** * Call api to get dashboard data * * @param {number} block Quantity of blocks to return @@ -148,7 +148,7 @@ const txApi = { }); }, - /* + /** * Call api to get graphviz * * @param {string} url URL to get graph data diff --git a/src/api/txMining.js b/src/api/txMining.js index 2d15fa129..8075da0e1 100644 --- a/src/api/txMining.js +++ b/src/api/txMining.js @@ -23,7 +23,7 @@ const txMiningApi = { * @param {Number} timeout Optional parameter to define the timeout of the submit job in seconds * * @return {Promise} - * @memberof txMiningApi + * @memberof ApiTxMining * @inner */ submitJob(tx, propagate, add_parents, timeout, resolve) { @@ -44,7 +44,7 @@ const txMiningApi = { * @param {String} job Job id * * @return {Promise} - * @memberof txMiningApi + * @memberof ApiTxMining * @inner */ getJobStatus(job, resolve) { @@ -62,7 +62,7 @@ const txMiningApi = { * @param {String} job Job id * * @return {Promise} - * @memberof txMiningApi + * @memberof ApiTxMining * @inner */ cancelJob(job, resolve) { @@ -73,6 +73,23 @@ const txMiningApi = { return Promise.reject(error); }); }, + + /** + * Get health information for the tx-mining-service + * + * @return {Promise} + * @memberof ApiTxMining + * @inner + */ + async getHealth() { + return new Promise((resolve, reject) => { + txMiningRequestClient(resolve).get(`health`).then((res) => { + resolve(res.data); + }, (err) => { + reject(err); + }); + }); + } }; export default txMiningApi; \ No newline at end of file diff --git a/src/api/version.js b/src/api/version.js index 2ba424957..89a321cb7 100644 --- a/src/api/version.js +++ b/src/api/version.js @@ -29,6 +29,23 @@ const versionApi = { }, (res) => { return Promise.reject(res); }); + }, + + /** + * Get version of full node running in connected server + * + * @return {Promise} + * @memberof ApiVersion + * @inner + */ + async asyncGetVersion() { + return new Promise((resolve, reject) => { + createRequestInstance(resolve).get(`version`).then((res) => { + resolve(res.data); + }, (err) => { + reject(err); + }); + }); } }; diff --git a/src/lib.ts b/src/lib.ts index d48dfde6c..d1808f589 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -6,6 +6,7 @@ import * as ErrorMessages from './errorMessages'; import walletApi from './api/wallet'; import txApi from './api/txApi'; import txMiningApi from './api/txMining'; +import healthApi from './api/health'; import versionApi from './api/version'; import * as axios from './api/axiosInstance'; import metadataApi from './api/metadataApi'; @@ -57,6 +58,7 @@ export { walletApi, txApi, txMiningApi, + healthApi, versionApi, metadataApi, errors,