From 786b85fde26f37505adc545e5f0ab0bbd2d48e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Mon, 24 Oct 2022 14:30:22 +0200 Subject: [PATCH] chore: add market-depth-provider unit tests (#1839) --- libs/market-depth/src/lib/depth-chart.tsx | 4 +- libs/market-depth/src/lib/index.ts | 2 +- .../src/lib/market-depth-provider.spec.ts | 76 +++++++++++++++++++ .../src/lib/market-depth-provider.ts | 35 +++------ .../src/lib/orderbook-manager.tsx | 2 +- .../src/lib/use-orderbook-data.ts | 4 +- 6 files changed, 93 insertions(+), 30 deletions(-) create mode 100644 libs/market-depth/src/lib/market-depth-provider.spec.ts diff --git a/libs/market-depth/src/lib/depth-chart.tsx b/libs/market-depth/src/lib/depth-chart.tsx index 19be91e42..25e7d9835 100644 --- a/libs/market-depth/src/lib/depth-chart.tsx +++ b/libs/market-depth/src/lib/depth-chart.tsx @@ -7,7 +7,7 @@ import { ThemeContext, getNumberFormat, } from '@vegaprotocol/react-helpers'; -import dataProvider from './market-depth-provider'; +import { marketDepthProvider } from './market-depth-provider'; import { useCallback, useEffect, @@ -115,7 +115,7 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => { ); const { data, error, loading } = useDataProvider({ - dataProvider, + dataProvider: marketDepthProvider, update, variables, }); diff --git a/libs/market-depth/src/lib/index.ts b/libs/market-depth/src/lib/index.ts index ee1bf316c..f042a171a 100644 --- a/libs/market-depth/src/lib/index.ts +++ b/libs/market-depth/src/lib/index.ts @@ -1,6 +1,6 @@ export * from './__generated___/MarketDepth'; export * from './depth-chart'; -export * from './market-depth-provider'; +export { marketDepthProvider } from './market-depth-provider'; export * from './orderbook-container'; export * from './orderbook-data'; export * from './orderbook-manager'; diff --git a/libs/market-depth/src/lib/market-depth-provider.spec.ts b/libs/market-depth/src/lib/market-depth-provider.spec.ts new file mode 100644 index 000000000..d10364df1 --- /dev/null +++ b/libs/market-depth/src/lib/market-depth-provider.spec.ts @@ -0,0 +1,76 @@ +import { update } from './market-depth-provider'; +import { captureException } from '@sentry/react'; + +const reload = jest.fn(); +jest.mock('@sentry/react', () => { + const original = jest.requireActual('@sentry/react'); // Step 2. + return { + ...original, + captureException: jest.fn(), + }; +}); + +jest.mock('./orderbook-data', () => ({ + updateLevels: jest.fn((arg) => arg), +})); + +describe('market depth provider update', () => { + it('omits overlapping updates', () => { + const data = { + id: '1', + depth: { + sequenceNumber: '1', + }, + }; + const delta = { + marketId: '2', + sequenceNumber: '', + previousSequenceNumber: '', + }; + const updatedData = update(data, [delta], reload); + expect(updatedData).toBe(data); + }); + + it('omits not matching market', () => { + const data = { + id: '1', + depth: { + sequenceNumber: '10', + }, + }; + const delta = [ + { + marketId: '1', + sequenceNumber: '5', + previousSequenceNumber: '', + }, + { + marketId: '1', + sequenceNumber: '10', + previousSequenceNumber: '', + }, + ]; + expect(update(data, delta.slice(0, 1), reload)).toBe(data); + expect(update(data, delta.slice(1, 2), reload)).toBe(data); + }); + + it('restarts and captureException when there is gap in updates', () => { + const data = { + id: '1', + depth: { + sequenceNumber: '10', + }, + }; + const delta = [ + { + marketId: '1', + sequenceNumber: '16', + previousSequenceNumber: '12', + }, + ]; + const updatedData = update(data, delta, reload); + expect(updatedData).toBe(data); + expect(reload).toBeCalled(); + expect(captureException).toBeCalled(); + }); +}); diff --git a/libs/market-depth/src/lib/market-depth-provider.ts b/libs/market-depth/src/lib/market-depth-provider.ts index 275172d52..a459cb544 100644 --- a/libs/market-depth/src/lib/market-depth-provider.ts +++ b/libs/market-depth/src/lib/market-depth-provider.ts @@ -12,40 +12,34 @@ import type { MarketDepthUpdateSubscription, } from './__generated___/MarketDepth'; -const sequenceNumbers: Record = {}; - -const update: Update< +export const update: Update< ReturnType, ReturnType > = (data, deltas, reload) => { if (!data) { - return; + return data; } for (const delta of deltas) { if (delta.marketId !== data.id) { continue; } - const currentSequenceNumber = Number(delta.sequenceNumber); - if (currentSequenceNumber <= sequenceNumbers[delta.marketId]) { + if (Number(delta.sequenceNumber) <= Number(data.depth.sequenceNumber)) { return data; } - if ( - delta.previousSequenceNumber !== - sequenceNumbers[delta.marketId].toString() - ) { + if (delta.previousSequenceNumber !== data.depth.sequenceNumber) { captureException( new Error( `Sequence number gap in marketsDepthUpdate for {data.id}, {sequenceNumbers[delta.marketId]} - {delta.previousSequenceNumber}` ) ); - delete sequenceNumbers[delta.marketId]; reload(); - return; + return data; } - sequenceNumbers[delta.marketId] = Number(currentSequenceNumber); const updatedData = { ...data, - depth: { ...data.depth }, + depth: { + ...data.depth, + }, }; if (delta.buy) { updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy); @@ -53,19 +47,14 @@ const update: Update< if (delta.sell) { updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell); } + updatedData.depth.sequenceNumber = delta.sequenceNumber; return updatedData; } return data; }; -const getData = (responseData: MarketDepthQuery) => { - if (responseData.market?.id) { - sequenceNumbers[responseData.market.id] = Number( - responseData.market.depth.sequenceNumber - ); - } - return responseData.market; -}; +const getData = (responseData: MarketDepthQuery) => responseData.market; + const getDelta = (subscriptionData: MarketDepthUpdateSubscription) => subscriptionData.marketsDepthUpdate; @@ -76,5 +65,3 @@ export const marketDepthProvider = makeDataProvider({ getData, getDelta, }); - -export default marketDepthProvider; diff --git a/libs/market-depth/src/lib/orderbook-manager.tsx b/libs/market-depth/src/lib/orderbook-manager.tsx index 5bf844152..86ddd476c 100644 --- a/libs/market-depth/src/lib/orderbook-manager.tsx +++ b/libs/market-depth/src/lib/orderbook-manager.tsx @@ -2,7 +2,7 @@ import throttle from 'lodash/throttle'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { Orderbook } from './orderbook'; import { useDataProvider } from '@vegaprotocol/react-helpers'; -import marketDepthProvider from './market-depth-provider'; +import { marketDepthProvider } from './market-depth-provider'; import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list'; import type { MarketData } from '@vegaprotocol/market-list'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/libs/market-depth/src/lib/use-orderbook-data.ts b/libs/market-depth/src/lib/use-orderbook-data.ts index 88ef4a8ba..a906f6af3 100644 --- a/libs/market-depth/src/lib/use-orderbook-data.ts +++ b/libs/market-depth/src/lib/use-orderbook-data.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import throttle from 'lodash/throttle'; import { useDataProvider } from '@vegaprotocol/react-helpers'; -import dataProvider from './market-depth-provider'; +import { marketDepthProvider } from './market-depth-provider'; import type { MarketDepthQuery } from './__generated___/MarketDepth'; interface Props { @@ -39,7 +39,7 @@ export const useOrderBookData = ({ ); const { data, error, loading } = useDataProvider({ - dataProvider, + dataProvider: marketDepthProvider, update, variables, });