moex-vibe/apps/backend/src/modules/moex-client/moex-market-data.client.spec.ts
Sergey Krylov 75fead68b8 refactor: split MoexClientService into domain-specific clients
- MoexHttpClient: infrastructure (axios, rate limiter, circuit breaker)
- MoexSecuritiesClient: search and security descriptions
- MoexMarketDataClient: share/bond market data and batch queries
- MoexCandlesClient: candle data
- MoexHistoryClient: share/bond history
- MoexDividendsClient: dividend data
- Removed @Global() from MoexClientModule
- Updated all 7 consumers with explicit DI
- All 141 tests passing
2026-06-25 20:49:17 +03:00

119 lines
5.4 KiB
TypeScript

import 'reflect-metadata';
import { MoexHttpClient } from './moex-http.client';
import { MoexMarketDataClient } from './moex-market-data.client';
describe('MoexMarketDataClient', () => {
let client: MoexMarketDataClient;
let request: ReturnType<typeof vi.fn>;
let extractTable: ReturnType<typeof vi.fn>;
beforeEach(() => {
request = vi.fn();
extractTable = vi.fn();
client = new MoexMarketDataClient({ request, extractTable } as unknown as MoexHttpClient);
});
describe('getShareMarketData', () => {
it('возвращает рыночные данные акции из securities и marketdata таблиц', async () => {
request.mockResolvedValue({});
extractTable
.mockReturnValueOnce([{ SECID: 'SBER', BOARDID: 'TQBR', SHORTNAME: 'Сбербанк', PREVPRICE: '320' }])
.mockReturnValueOnce([{ SECID: 'SBER', BOARDID: 'TQBR', BID: '321', OFFER: '322', OPEN: '320', LOW: '319', HIGH: '323', LAST: '322.35', LASTCHANGE: '1.15', LASTCHANGEPRCNT: '0.36', VOLTODAY: '1925163', VALTODAY: '620184479', WAPRICE: '321.9', NUMTRADES: '12345', ISSUECAPITALIZATION: '6958336818320', TRADINGSTATUS: 'T', UPDATETIME: '10:30:00' }]);
const result = await client.getShareMarketData('SBER');
expect(request).toHaveBeenCalledWith('/engines/stock/markets/shares/securities/SBER', { boards: 'TQBR' });
expect(result).toMatchObject({ secid: 'SBER', boardid: 'TQBR', shortName: 'Сбербанк', last: 322.35, bid: 321, offer: 322 });
});
it('возвращает null если бумага не найдена', async () => {
request.mockResolvedValue({});
extractTable.mockReturnValueOnce([]).mockReturnValueOnce([]);
const result = await client.getShareMarketData('INVALID');
expect(result).toBeNull();
});
});
describe('getShareMarketDataBatch', () => {
it('возвращает массив рыночных данных для нескольких бумаг', async () => {
request.mockResolvedValue({});
extractTable
.mockReturnValueOnce([
{ SECID: 'SBER', BOARDID: 'TQBR', SHORTNAME: 'Сбербанк', PREVPRICE: '320' },
{ SECID: 'VTBR', BOARDID: 'TQBR', SHORTNAME: 'ВТБ', PREVPRICE: '50' },
])
.mockReturnValueOnce([
{ SECID: 'SBER', BOARDID: 'TQBR', LAST: '322', BID: '321', OFFER: '323' },
{ SECID: 'VTBR', BOARDID: 'TQBR', LAST: '50.5', BID: '50.1', OFFER: '50.8' },
]);
const results = await client.getShareMarketDataBatch(['SBER', 'VTBR']);
expect(results).toHaveLength(2);
expect(results[0].secid).toBe('SBER');
expect(results[1].secid).toBe('VTBR');
});
});
describe('getBondData', () => {
it('возвращает данные облигации из securities таблицы', async () => {
request.mockResolvedValue({});
extractTable.mockReturnValueOnce([
{ SECID: 'SU26238RMFS4', BOARDID: 'TQCB', SHORTNAME: 'ОФЗ 26238', PREVWAPRICE: '98.5', COUPONVALUE: '34.5', NEXTCOUPON: '2025-01-15', MATDATE: '2041-05-15', FACEVALUE: '1000', ISIN: 'RU000A1038T7' },
]);
const result = await client.getBondData('SU26238RMFS4');
expect(request).toHaveBeenCalledWith('/engines/stock/markets/bonds/securities/SU26238RMFS4', { boards: 'TQCB' });
expect(result).toMatchObject({ secid: 'SU26238RMFS4', shortName: 'ОФЗ 26238' });
});
it('возвращает null если облигация не найдена', async () => {
request.mockResolvedValue({});
extractTable.mockReturnValueOnce([]);
const result = await client.getBondData('INVALID');
expect(result).toBeNull();
});
});
describe('getBondMarketData', () => {
it('возвращает рыночные данные облигации из marketdata таблицы', async () => {
request.mockResolvedValue({});
extractTable.mockReturnValueOnce([{ SECID: 'SU26238RMFS4', BOARDID: 'TQCB', LAST: '98.5', BID: '98', OFFER: '99', YIELD: '7.5', DURATION: '1500', VOLTODAY: '1000', VALTODAY: '98500', NUMTRADES: '50', TRADINGSTATUS: 'T', UPDATETIME: '10:30:00' }]);
const result = await client.getBondMarketData('SU26238RMFS4');
expect(result).toMatchObject({ secid: 'SU26238RMFS4', last: 98.5, bid: 98, offer: 99, yield: 7.5 });
});
it('возвращает null если marketdata не найдена', async () => {
request.mockResolvedValue({});
extractTable.mockReturnValueOnce([]);
const result = await client.getBondMarketData('INVALID');
expect(result).toBeNull();
});
});
describe('getBondPositionDataBatch', () => {
it('возвращает массив позиций по облигациям', async () => {
request.mockResolvedValue({});
extractTable
.mockReturnValueOnce([
{ SECID: 'SU26238RMFS4', BOARDID: 'TQCB', SHORTNAME: 'ОФЗ 26238', COUPONVALUE: '34.5', COUPONPERCENT: '7', NEXTCOUPON: '2025-01-15', MATDATE: '2041-05-15', FACEVALUE: '1000', ISIN: 'RU000A1038T7' },
])
.mockReturnValueOnce([
{ SECID: 'SU26238RMFS4', BOARDID: 'TQCB', LAST: '98.5', YIELD: '7.5', DURATION: '1500', BID: '98', OFFER: '99' },
]);
const results = await client.getBondPositionDataBatch(['SU26238RMFS4']);
expect(results).toHaveLength(1);
expect(results[0].secid).toBe('SU26238RMFS4');
expect(results[0].price).toBe(98.5);
});
});
});