test: make backend service specs deterministic
This commit is contained in:
parent
c994b6a2fb
commit
974c83d67e
@ -1,41 +1,147 @@
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { BondsService } from './bonds.service';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import configuration from '../../config/configuration';
|
||||
|
||||
describe('BondsService', () => {
|
||||
let service: BondsService;
|
||||
let moexClient: Pick<MoexClientService, 'getBondData' | 'getBondMarketData'>;
|
||||
let cache: Pick<CacheService, 'getOrFetch'>;
|
||||
|
||||
beforeEach(async () => {
|
||||
moexClient = {
|
||||
getBondData: vi.fn(),
|
||||
getBondMarketData: vi.fn(),
|
||||
};
|
||||
cache = {
|
||||
getOrFetch: vi.fn(async (_keyPrefix, _keyParts, fetchFn) => ({
|
||||
data: await fetchFn(),
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
})),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot({ load: [configuration] })],
|
||||
providers: [
|
||||
BondsService,
|
||||
MoexClientService,
|
||||
{
|
||||
provide: 'CACHE_MANAGER',
|
||||
useValue: {
|
||||
get: () => undefined,
|
||||
set: () => Promise.resolve(),
|
||||
del: () => Promise.resolve(),
|
||||
},
|
||||
},
|
||||
CacheService,
|
||||
{ provide: MoexClientService, useValue: moexClient },
|
||||
{ provide: CacheService, useValue: cache },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<BondsService>(BondsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
it('returns normalized SU26238RMFS5 bond spec and market data without live MOEX dependency', async () => {
|
||||
vi.mocked(moexClient.getBondData).mockResolvedValue({
|
||||
secid: 'SU26238RMFS5',
|
||||
boardid: 'TQCB',
|
||||
shortName: 'ОФЗ 26238',
|
||||
prevWaprice: 73.2,
|
||||
yieldAtPrevWaprice: 14.1,
|
||||
couponValue: 35.4,
|
||||
nextCoupon: '2026-06-24',
|
||||
accruedInt: 34.1,
|
||||
prevPrice: 73,
|
||||
lotSize: 1,
|
||||
faceValue: 1000,
|
||||
matDate: '2041-05-15',
|
||||
couponPeriod: 182,
|
||||
issueSize: 150000000,
|
||||
isin: 'RU000A1038V6',
|
||||
couponPercent: 7.1,
|
||||
offerDate: null,
|
||||
buybackDate: null,
|
||||
bondType: 'ofz',
|
||||
bondSubType: 'fixed',
|
||||
listLevel: 1,
|
||||
});
|
||||
vi.mocked(moexClient.getBondMarketData).mockResolvedValue({
|
||||
secid: 'SU26238RMFS5',
|
||||
bid: 72.9,
|
||||
offer: 73.1,
|
||||
open: 72.8,
|
||||
low: 72.5,
|
||||
high: 73.4,
|
||||
last: 73.05,
|
||||
yield: 14.2,
|
||||
waprice: 73,
|
||||
yieldAtWaprice: 14.15,
|
||||
duration: 2250,
|
||||
volume: 10000,
|
||||
value: 7305000,
|
||||
numtrades: 450,
|
||||
tradingStatus: 'T',
|
||||
updateTime: '18:45:00',
|
||||
});
|
||||
|
||||
const result = await service.getBond('SU26238RMFS5');
|
||||
|
||||
expect(cache.getOrFetch).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'bond',
|
||||
['SU26238RMFS5'],
|
||||
expect.any(Function),
|
||||
'securityTtl',
|
||||
);
|
||||
expect(cache.getOrFetch).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'marketdata',
|
||||
['bonds', 'SU26238RMFS5'],
|
||||
expect.any(Function),
|
||||
'marketDataTtl',
|
||||
);
|
||||
expect(moexClient.getBondData).toHaveBeenCalledWith('SU26238RMFS5');
|
||||
expect(moexClient.getBondMarketData).toHaveBeenCalledWith('SU26238RMFS5');
|
||||
expect(result).toMatchObject({
|
||||
data: {
|
||||
secid: 'SU26238RMFS5',
|
||||
isin: 'RU000A1038V6',
|
||||
name: 'ОФЗ 26238',
|
||||
shortName: 'ОФЗ 26238',
|
||||
latName: null,
|
||||
listLevel: 1,
|
||||
issueSize: 150000000,
|
||||
faceValue: 1000,
|
||||
faceUnit: 'RUB',
|
||||
matDate: '2041-05-15',
|
||||
couponValue: 35.4,
|
||||
couponPercent: 7.1,
|
||||
couponPeriod: 182,
|
||||
nextCoupon: '2026-06-24',
|
||||
accruedInt: 34.1,
|
||||
bondType: 'ofz',
|
||||
bondSubType: 'fixed',
|
||||
offerDate: null,
|
||||
buybackDate: null,
|
||||
marketData: {
|
||||
price: 73.05,
|
||||
yieldToMaturity: 14.2,
|
||||
duration: 2250,
|
||||
accruedInt: 34.1,
|
||||
couponValue: 35.4,
|
||||
couponPercent: 7.1,
|
||||
nextCouponDate: '2026-06-24',
|
||||
open: 72.8,
|
||||
high: 73.4,
|
||||
low: 72.5,
|
||||
volume: 10000,
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
expect(result.data.marketData.updatedAt).toMatch(/T18:45:00$/);
|
||||
});
|
||||
|
||||
it('should return OFZ bond data for SU26207RMFS9', async () => {
|
||||
const result = await service.getBond('SU26207RMFS9');
|
||||
expect(result.data.secid).toBe('SU26207RMFS9');
|
||||
expect(result.data.marketData).toBeDefined();
|
||||
}, 15000);
|
||||
it('throws NotFoundException when bond data is missing', async () => {
|
||||
vi.mocked(moexClient.getBondData).mockResolvedValue(null);
|
||||
|
||||
await expect(service.getBond('UNKNOWN')).rejects.toBeInstanceOf(NotFoundException);
|
||||
expect(cache.getOrFetch).toHaveBeenCalledTimes(1);
|
||||
expect(moexClient.getBondMarketData).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,40 +1,51 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { CandlesService } from './candles.service';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import configuration from '../../config/configuration';
|
||||
import { CandleInterval } from './dto/candles-query.dto';
|
||||
|
||||
describe('CandlesService', () => {
|
||||
let service: CandlesService;
|
||||
let moexClient: Pick<MoexClientService, 'getCandles'>;
|
||||
let cache: Pick<CacheService, 'getOrFetch'>;
|
||||
|
||||
beforeEach(async () => {
|
||||
moexClient = {
|
||||
getCandles: vi.fn(),
|
||||
};
|
||||
cache = {
|
||||
getOrFetch: vi.fn(async (_keyPrefix, _keyParts, fetchFn) => ({
|
||||
data: await fetchFn(),
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
})),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot({ load: [configuration] })],
|
||||
providers: [
|
||||
CandlesService,
|
||||
MoexClientService,
|
||||
{
|
||||
provide: 'CACHE_MANAGER',
|
||||
useValue: {
|
||||
get: () => undefined,
|
||||
set: () => Promise.resolve(),
|
||||
del: () => Promise.resolve(),
|
||||
},
|
||||
},
|
||||
CacheService,
|
||||
{ provide: MoexClientService, useValue: moexClient },
|
||||
{ provide: CacheService, useValue: cache },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CandlesService>(CandlesService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
it('uses MOEX interval 24 for daily share candles and maps output envelope', async () => {
|
||||
vi.mocked(moexClient.getCandles).mockResolvedValue([
|
||||
{
|
||||
open: 320,
|
||||
high: 325,
|
||||
low: 318,
|
||||
close: 323,
|
||||
volume: 1500000,
|
||||
value: 480000000,
|
||||
begin: '2026-05-01 00:00:00',
|
||||
end: '2026-05-01 23:59:59',
|
||||
},
|
||||
]);
|
||||
|
||||
it('should return daily candles for SBER', async () => {
|
||||
const result = await service.getCandles(
|
||||
'shares',
|
||||
'SBER',
|
||||
@ -42,7 +53,65 @@ describe('CandlesService', () => {
|
||||
'2026-05-01',
|
||||
'2026-06-01',
|
||||
);
|
||||
expect(result.data.length).toBeGreaterThan(0);
|
||||
expect(result.data[0].open).toBeDefined();
|
||||
}, 15000);
|
||||
|
||||
expect(cache.getOrFetch).toHaveBeenCalledWith(
|
||||
'candles',
|
||||
['shares', 'SBER', '24', '2026-05-01', '2026-06-01'],
|
||||
expect.any(Function),
|
||||
'candlesTtl',
|
||||
);
|
||||
expect(moexClient.getCandles).toHaveBeenCalledWith(
|
||||
'stock',
|
||||
'shares',
|
||||
'SBER',
|
||||
24,
|
||||
'2026-05-01',
|
||||
'2026-06-01',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
data: [
|
||||
{
|
||||
open: 320,
|
||||
high: 325,
|
||||
low: 318,
|
||||
close: 323,
|
||||
volume: 1500000,
|
||||
value: 480000000,
|
||||
begin: '2026-05-01 00:00:00',
|
||||
end: '2026-05-01 23:59:59',
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('uses MOEX interval 60 for hourly bond candles without live MOEX dependency', async () => {
|
||||
vi.mocked(moexClient.getCandles).mockResolvedValue([]);
|
||||
|
||||
await service.getCandles(
|
||||
'bonds',
|
||||
'SU26238RMFS5',
|
||||
CandleInterval.HOUR,
|
||||
'2026-05-01',
|
||||
'2026-06-01',
|
||||
);
|
||||
|
||||
expect(cache.getOrFetch).toHaveBeenCalledWith(
|
||||
'candles',
|
||||
['bonds', 'SU26238RMFS5', '60', '2026-05-01', '2026-06-01'],
|
||||
expect.any(Function),
|
||||
'candlesTtl',
|
||||
);
|
||||
expect(moexClient.getCandles).toHaveBeenCalledWith(
|
||||
'stock',
|
||||
'bonds',
|
||||
'SU26238RMFS5',
|
||||
60,
|
||||
'2026-05-01',
|
||||
'2026-06-01',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,7 +6,6 @@ import { ScreenerType } from './dto/screener-query.dto';
|
||||
|
||||
describe('ScreenerService', () => {
|
||||
let service: ScreenerService;
|
||||
let moexClient: MoexClientService;
|
||||
let cache: CacheService;
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -30,7 +29,6 @@ describe('ScreenerService', () => {
|
||||
}).compile();
|
||||
|
||||
service = module.get<ScreenerService>(ScreenerService);
|
||||
moexClient = module.get<MoexClientService>(MoexClientService);
|
||||
cache = module.get<CacheService>(CacheService);
|
||||
});
|
||||
|
||||
|
||||
@ -1,38 +1,174 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SecuritiesService } from './securities.service';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import configuration from '../../config/configuration';
|
||||
import { SecurityType } from './dto/search-query.dto';
|
||||
|
||||
describe('SecuritiesService', () => {
|
||||
let service: SecuritiesService;
|
||||
let moexClient: Pick<MoexClientService, 'searchSecurities'>;
|
||||
let cache: Pick<CacheService, 'getOrFetch'>;
|
||||
|
||||
beforeEach(async () => {
|
||||
moexClient = {
|
||||
searchSecurities: vi.fn(),
|
||||
};
|
||||
cache = {
|
||||
getOrFetch: vi.fn(async (_keyPrefix, _keyParts, fetchFn) => ({
|
||||
data: await fetchFn(),
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
})),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot({ load: [configuration] })],
|
||||
providers: [
|
||||
SecuritiesService,
|
||||
MoexClientService,
|
||||
{
|
||||
provide: 'CACHE_MANAGER',
|
||||
useValue: {
|
||||
get: () => undefined,
|
||||
set: () => Promise.resolve(),
|
||||
del: () => Promise.resolve(),
|
||||
},
|
||||
},
|
||||
CacheService,
|
||||
{ provide: MoexClientService, useValue: moexClient },
|
||||
{ provide: CacheService, useValue: cache },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SecuritiesService>(SecuritiesService);
|
||||
});
|
||||
|
||||
it('should return search results for SBER', async () => {
|
||||
const results = await service.search('SBER', SecurityType.ALL, 5);
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].secid).toBeDefined();
|
||||
}, 15000);
|
||||
it('returns supported securities only and normalizes SUR currency to RUB', async () => {
|
||||
vi.mocked(moexClient.searchSecurities).mockResolvedValue([
|
||||
{
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
name: 'Сбербанк России ПАО ао',
|
||||
shortName: 'Сбербанк',
|
||||
latName: 'Sberbank',
|
||||
listLevel: 1,
|
||||
issueSize: 21586948000,
|
||||
faceValue: 3,
|
||||
faceUnit: 'SUR',
|
||||
issueDate: '2007-07-20',
|
||||
typeName: 'Акция обыкновенная',
|
||||
group: 'stock_shares',
|
||||
type: 'common_share',
|
||||
isQualifiedInvestors: false,
|
||||
morningSession: true,
|
||||
eveningSession: true,
|
||||
},
|
||||
{
|
||||
secid: 'SU26238RMFS5',
|
||||
isin: 'RU000A1038V6',
|
||||
name: 'ОФЗ 26238',
|
||||
shortName: 'ОФЗ 26238',
|
||||
latName: null,
|
||||
listLevel: 1,
|
||||
issueSize: 100000000,
|
||||
faceValue: 1000,
|
||||
faceUnit: 'RUB',
|
||||
issueDate: '2021-06-23',
|
||||
typeName: 'Государственная облигация',
|
||||
group: 'stock_bonds',
|
||||
type: 'ofz_bond',
|
||||
isQualifiedInvestors: false,
|
||||
morningSession: false,
|
||||
eveningSession: false,
|
||||
},
|
||||
{
|
||||
secid: 'SiM6',
|
||||
isin: '',
|
||||
name: 'USD/RUB Futures',
|
||||
shortName: 'SiM6',
|
||||
latName: null,
|
||||
listLevel: 0,
|
||||
issueSize: 0,
|
||||
faceValue: 0,
|
||||
faceUnit: '',
|
||||
issueDate: '',
|
||||
typeName: 'Фьючерс',
|
||||
group: 'futures',
|
||||
type: 'futures',
|
||||
isQualifiedInvestors: false,
|
||||
morningSession: false,
|
||||
eveningSession: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const results = await service.search('SbEr', SecurityType.ALL, 10);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
shortName: 'Сбербанк',
|
||||
type: 'share',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
{
|
||||
secid: 'SU26238RMFS5',
|
||||
isin: 'RU000A1038V6',
|
||||
shortName: 'ОФЗ 26238',
|
||||
type: 'bond',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
]);
|
||||
expect(cache.getOrFetch).toHaveBeenCalledWith(
|
||||
'search',
|
||||
['sber'],
|
||||
expect.any(Function),
|
||||
'searchTtl',
|
||||
);
|
||||
expect(moexClient.searchSecurities).toHaveBeenCalledWith('SbEr');
|
||||
});
|
||||
|
||||
it('filters by type and applies limit without live MOEX dependency', async () => {
|
||||
vi.mocked(cache.getOrFetch).mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
shortName: 'Сбербанк',
|
||||
type: 'share',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
{
|
||||
secid: 'GAZP',
|
||||
isin: 'RU0007661625',
|
||||
shortName: 'Газпром',
|
||||
type: 'share',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
{
|
||||
secid: 'SU26238RMFS5',
|
||||
isin: 'RU000A1038V6',
|
||||
shortName: 'ОФЗ 26238',
|
||||
type: 'bond',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
],
|
||||
fromCache: true,
|
||||
cachedAt: null,
|
||||
});
|
||||
|
||||
const results = await service.search('ru', SecurityType.SHARE, 1);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
shortName: 'Сбербанк',
|
||||
type: 'share',
|
||||
listLevel: 1,
|
||||
currency: 'RUB',
|
||||
price: null,
|
||||
},
|
||||
]);
|
||||
expect(moexClient.searchSecurities).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,41 +1,135 @@
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SharesService } from './shares.service';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import configuration from '../../config/configuration';
|
||||
|
||||
describe('SharesService', () => {
|
||||
let service: SharesService;
|
||||
let moexClient: Pick<MoexClientService, 'getSecurityDescription' | 'getShareMarketData'>;
|
||||
let cache: Pick<CacheService, 'getOrFetch'>;
|
||||
|
||||
beforeEach(async () => {
|
||||
moexClient = {
|
||||
getSecurityDescription: vi.fn(),
|
||||
getShareMarketData: vi.fn(),
|
||||
};
|
||||
cache = {
|
||||
getOrFetch: vi.fn(async (_keyPrefix, _keyParts, fetchFn) => ({
|
||||
data: await fetchFn(),
|
||||
fromCache: false,
|
||||
cachedAt: '2026-06-15T00:00:00.000Z',
|
||||
})),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot({ load: [configuration] })],
|
||||
providers: [
|
||||
SharesService,
|
||||
MoexClientService,
|
||||
{
|
||||
provide: 'CACHE_MANAGER',
|
||||
useValue: {
|
||||
get: () => undefined,
|
||||
set: () => Promise.resolve(),
|
||||
del: () => Promise.resolve(),
|
||||
},
|
||||
},
|
||||
CacheService,
|
||||
{ provide: MoexClientService, useValue: moexClient },
|
||||
{ provide: CacheService, useValue: cache },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SharesService>(SharesService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
it('returns normalized SBER share spec and market data without live MOEX dependency', async () => {
|
||||
vi.mocked(moexClient.getSecurityDescription).mockResolvedValue({
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
name: 'Сбербанк России ПАО ао',
|
||||
shortName: 'Сбербанк',
|
||||
latName: 'Sberbank',
|
||||
listLevel: 1,
|
||||
issueSize: 21586948000,
|
||||
faceValue: 3,
|
||||
faceUnit: 'SUR',
|
||||
issueDate: '2007-07-20',
|
||||
typeName: 'Акция обыкновенная',
|
||||
group: 'stock_shares',
|
||||
type: 'common_share',
|
||||
isQualifiedInvestors: false,
|
||||
morningSession: true,
|
||||
eveningSession: true,
|
||||
});
|
||||
vi.mocked(moexClient.getShareMarketData).mockResolvedValue({
|
||||
secid: 'SBER',
|
||||
boardid: 'TQBR',
|
||||
shortName: 'Сбербанк',
|
||||
bid: 320,
|
||||
offer: 321,
|
||||
open: 318,
|
||||
low: 317,
|
||||
high: 325,
|
||||
last: 323,
|
||||
lastChange: 4,
|
||||
lastChangePrcnt: 1.25,
|
||||
volume: 1500000,
|
||||
value: 480000000,
|
||||
waprice: 321,
|
||||
numtrades: 4200,
|
||||
issueCapitalization: 6900000000000,
|
||||
tradingStatus: 'T',
|
||||
updateTime: '18:45:00',
|
||||
});
|
||||
|
||||
const result = await service.getShare('SBER');
|
||||
|
||||
expect(moexClient.getSecurityDescription).toHaveBeenCalledWith('SBER');
|
||||
expect(cache.getOrFetch).toHaveBeenCalledWith(
|
||||
'marketdata',
|
||||
['shares', 'SBER'],
|
||||
expect.any(Function),
|
||||
'marketDataTtl',
|
||||
);
|
||||
expect(moexClient.getShareMarketData).toHaveBeenCalledWith('SBER');
|
||||
expect(result).toMatchObject({
|
||||
secid: 'SBER',
|
||||
isin: 'RU0009029540',
|
||||
name: 'Сбербанк России ПАО ао',
|
||||
shortName: 'Сбербанк',
|
||||
latName: 'Sberbank',
|
||||
listLevel: 1,
|
||||
issueSize: 21586948000,
|
||||
faceValue: 3,
|
||||
faceUnit: 'RUB',
|
||||
type: 'common_share',
|
||||
marketData: {
|
||||
price: 323,
|
||||
change: 4,
|
||||
changePercent: 1.25,
|
||||
open: 318,
|
||||
high: 325,
|
||||
low: 317,
|
||||
volume: 1500000,
|
||||
value: 480000000,
|
||||
issueCapitalization: 6900000000000,
|
||||
},
|
||||
});
|
||||
expect(result.marketData.updatedAt).toMatch(/T18:45:00$/);
|
||||
});
|
||||
|
||||
it('should return SBER share data', async () => {
|
||||
const share = await service.getShare('SBER');
|
||||
expect(share.secid).toBe('SBER');
|
||||
expect(share.marketData).toBeDefined();
|
||||
}, 15000);
|
||||
it('throws NotFoundException for non-share security', async () => {
|
||||
vi.mocked(moexClient.getSecurityDescription).mockResolvedValue({
|
||||
secid: 'SU26238RMFS5',
|
||||
isin: 'RU000A1038V6',
|
||||
name: 'ОФЗ 26238',
|
||||
shortName: 'ОФЗ 26238',
|
||||
latName: null,
|
||||
listLevel: 1,
|
||||
issueSize: 100000000,
|
||||
faceValue: 1000,
|
||||
faceUnit: 'RUB',
|
||||
issueDate: '2021-06-23',
|
||||
typeName: 'Государственная облигация',
|
||||
group: 'stock_bonds',
|
||||
type: 'ofz_bond',
|
||||
isQualifiedInvestors: false,
|
||||
morningSession: false,
|
||||
eveningSession: false,
|
||||
});
|
||||
|
||||
await expect(service.getShare('SU26238RMFS5')).rejects.toBeInstanceOf(NotFoundException);
|
||||
expect(cache.getOrFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user