Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions __tests__/shared/services/money.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-env jest */

jest.mock('isomorphic-fetch', () => jest.fn());
jest.mock('money', () => ({
convert: jest.fn(() => 42),
}));
jest.mock('topcoder-react-utils', () => ({
config: {
CDN: { PUBLIC: 'https://cdn.example.com' },
},
isomorphy: {
isClientSide: () => false,
},
}));

const fetch = require('isomorphic-fetch');
const fx = require('money');

fetch.mockResolvedValue({
ok: false,
statusText: 'Internal Server Error',
});

const money = jest.requireActual('../../../src/shared/services/money');

describe('money service', () => {
test('keeps background refresh failures from becoming unhandled rejections', async () => {
expect(money.getRatesNow()).toEqual({ timestamp: 0 });
expect(money.convertNow(10, 'EUR')).toBe(42);

await Promise.resolve();
await Promise.resolve();

expect(fetch).toHaveBeenCalledTimes(3);
expect(fx.convert).toHaveBeenCalledWith(10, { from: 'USD', to: 'EUR' });
});
});
48 changes: 24 additions & 24 deletions src/shared/services/money.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,20 @@ async function updateCache() {
fx.rates = cache.rates;
}

try {
updateCache();
} catch (error) {
// exchange-rates failed, reason: socket hang up
/**
* Starts a cache refresh without blocking synchronous callers.
*
* Used during module initialization and by the synchronous public methods.
* Refresh failures are consumed so existing cached rates remain available
* instead of creating an unhandled Promise rejection.
* @return {void}
*/
function refreshCacheInBackground() {
updateCache().catch(_.noop);
}

refreshCacheInBackground();

/**
* Converts specified amount of money to another currency.
* @param {Number} amount Amount of money to convert.
Expand All @@ -61,21 +69,16 @@ export async function convert(amount, to, from = 'USD') {
}

/**
* Same as convert(..), but works syncroneously (using cached rates).
* This function still triggers refreshement of the cached rates if necessary,
* but it does not wait for the result, and just uses cached rates for the
* actual conversion. It is safe to use anyway
* @param {Number} amount
* @param {String} to
* @param {String} from
* @return {Number}
* Converts an amount synchronously using cached rates while triggering a
* non-blocking refresh when the cache is stale. Refresh failures are ignored
* and leave the existing cache in place.
* @param {Number} amount Amount of money to convert.
* @param {String} to Target currency (3-letter code such as USD or EUR).
* @param {String} from Original currency. Defaults to USD.
* @return {Number} Converted amount.
*/
export function convertNow(amount, to, from = 'USD') {
try {
updateCache();
} catch (error) {
// exchange-rates failed, reason: socket hang up
}
refreshCacheInBackground();
return fx.convert(amount, { from, to });
}

Expand All @@ -93,14 +96,11 @@ export async function getRates() {
}

/**
* Same as getRates(..) but works syncroneously, using the cached rates.
* @return {Promise}
* Returns cached exchange rates synchronously while triggering a non-blocking
* refresh when the cache is stale. Refresh failures leave the cache unchanged.
* @return {Object} A clone of the cached exchange-rate data.
*/
export function getRatesNow() {
try {
updateCache();
} catch (error) {
// exchange-rates failed, reason: socket hang up
}
refreshCacheInBackground();
return _.cloneDeep(cache);
}
Loading