chore: add market-depth-provider unit tests (#1839)

This commit is contained in:
Bartłomiej Głownia 2022-10-24 14:30:22 +02:00 committed by GitHub
parent ee3b9a56c9
commit 786b85fde2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 30 deletions

View File

@ -7,7 +7,7 @@ import {
ThemeContext, ThemeContext,
getNumberFormat, getNumberFormat,
} from '@vegaprotocol/react-helpers'; } from '@vegaprotocol/react-helpers';
import dataProvider from './market-depth-provider'; import { marketDepthProvider } from './market-depth-provider';
import { import {
useCallback, useCallback,
useEffect, useEffect,
@ -115,7 +115,7 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
); );
const { data, error, loading } = useDataProvider({ const { data, error, loading } = useDataProvider({
dataProvider, dataProvider: marketDepthProvider,
update, update,
variables, variables,
}); });

View File

@ -1,6 +1,6 @@
export * from './__generated___/MarketDepth'; export * from './__generated___/MarketDepth';
export * from './depth-chart'; export * from './depth-chart';
export * from './market-depth-provider'; export { marketDepthProvider } from './market-depth-provider';
export * from './orderbook-container'; export * from './orderbook-container';
export * from './orderbook-data'; export * from './orderbook-data';
export * from './orderbook-manager'; export * from './orderbook-manager';

View File

@ -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();
});
});

View File

@ -12,40 +12,34 @@ import type {
MarketDepthUpdateSubscription, MarketDepthUpdateSubscription,
} from './__generated___/MarketDepth'; } from './__generated___/MarketDepth';
const sequenceNumbers: Record<string, number> = {}; export const update: Update<
const update: Update<
ReturnType<typeof getData>, ReturnType<typeof getData>,
ReturnType<typeof getDelta> ReturnType<typeof getDelta>
> = (data, deltas, reload) => { > = (data, deltas, reload) => {
if (!data) { if (!data) {
return; return data;
} }
for (const delta of deltas) { for (const delta of deltas) {
if (delta.marketId !== data.id) { if (delta.marketId !== data.id) {
continue; continue;
} }
const currentSequenceNumber = Number(delta.sequenceNumber); if (Number(delta.sequenceNumber) <= Number(data.depth.sequenceNumber)) {
if (currentSequenceNumber <= sequenceNumbers[delta.marketId]) {
return data; return data;
} }
if ( if (delta.previousSequenceNumber !== data.depth.sequenceNumber) {
delta.previousSequenceNumber !==
sequenceNumbers[delta.marketId].toString()
) {
captureException( captureException(
new Error( new Error(
`Sequence number gap in marketsDepthUpdate for {data.id}, {sequenceNumbers[delta.marketId]} - {delta.previousSequenceNumber}` `Sequence number gap in marketsDepthUpdate for {data.id}, {sequenceNumbers[delta.marketId]} - {delta.previousSequenceNumber}`
) )
); );
delete sequenceNumbers[delta.marketId];
reload(); reload();
return; return data;
} }
sequenceNumbers[delta.marketId] = Number(currentSequenceNumber);
const updatedData = { const updatedData = {
...data, ...data,
depth: { ...data.depth }, depth: {
...data.depth,
},
}; };
if (delta.buy) { if (delta.buy) {
updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy); updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy);
@ -53,19 +47,14 @@ const update: Update<
if (delta.sell) { if (delta.sell) {
updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell); updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell);
} }
updatedData.depth.sequenceNumber = delta.sequenceNumber;
return updatedData; return updatedData;
} }
return data; return data;
}; };
const getData = (responseData: MarketDepthQuery) => { const getData = (responseData: MarketDepthQuery) => responseData.market;
if (responseData.market?.id) {
sequenceNumbers[responseData.market.id] = Number(
responseData.market.depth.sequenceNumber
);
}
return responseData.market;
};
const getDelta = (subscriptionData: MarketDepthUpdateSubscription) => const getDelta = (subscriptionData: MarketDepthUpdateSubscription) =>
subscriptionData.marketsDepthUpdate; subscriptionData.marketsDepthUpdate;
@ -76,5 +65,3 @@ export const marketDepthProvider = makeDataProvider({
getData, getData,
getDelta, getDelta,
}); });
export default marketDepthProvider;

View File

@ -2,7 +2,7 @@ import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Orderbook } from './orderbook'; import { Orderbook } from './orderbook';
import { useDataProvider } from '@vegaprotocol/react-helpers'; 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 { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list'; import type { MarketData } from '@vegaprotocol/market-list';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import { useDataProvider } from '@vegaprotocol/react-helpers'; import { useDataProvider } from '@vegaprotocol/react-helpers';
import dataProvider from './market-depth-provider'; import { marketDepthProvider } from './market-depth-provider';
import type { MarketDepthQuery } from './__generated___/MarketDepth'; import type { MarketDepthQuery } from './__generated___/MarketDepth';
interface Props { interface Props {
@ -39,7 +39,7 @@ export const useOrderBookData = ({
); );
const { data, error, loading } = useDataProvider({ const { data, error, loading } = useDataProvider({
dataProvider, dataProvider: marketDepthProvider,
update, update,
variables, variables,
}); });