chore: strengthen backend eslint profile

This commit is contained in:
2026-09-28 11:04:18 +03:00
parent ca5dbcb382
commit 9b3558f96c
179 changed files with 4485 additions and 2716 deletions
+44 -33
View File
@@ -1,34 +1,33 @@
import { MoexClient } from '../src/integrations/moex/moex.api';
import { NcrRatingsClient } from '../src/integrations/ncr/ncr.api';
import type {
StockCandlesResourceDto,
StockCompanyResourceDto,
StockQuoteResourceDto,
} from '../src/modules/investments/instruments/stocks/presentation/stock.dto';
import type {
BondMetricsResourceDto,
BondQuoteResourceDto,
} from '../src/modules/investments/instruments/bonds/presentation/bond.dto';
import { InstrumentMetadataService } from '../src/integrations/tbank/instrument-metadata.service';
import assert from 'node:assert/strict';
import type { Server } from 'node:http';
import { INestApplication } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Test } from '@nestjs/testing';
import assert from 'node:assert/strict';
import { setTimeout as delay } from 'node:timers/promises';
import type { Server } from 'node:http';
import request from 'supertest';
import { AppModule } from '../src/app.module';
import { configureApp } from '../src/main';
import { PrismaService } from '../src/prisma/prisma.service';
import { TbankClient } from '../src/integrations/tbank/tbank.client';
import { MoexClient } from '../src/integrations/moex/moex.api';
import { NcrRatingsClient } from '../src/integrations/ncr/ncr.api';
import { InstrumentMetadataService } from '../src/integrations/tbank/instrument-metadata.service';
import { TbankTokenService } from '../src/integrations/tbank/tbank-token.service';
import { TbankClient } from '../src/integrations/tbank/tbank.client';
import { configureApp } from '../src/main';
import { CatalogEnrichmentService } from '../src/modules/investments/instruments/application/catalog-enrichment.service';
import {
type BondMetricsResourceDto,
type BondQuoteResourceDto, type BondSearchResourceDto,
} from '../src/modules/investments/instruments/bonds/presentation/bond.dto';
import { CatalogDiscoveryRepository } from '../src/modules/investments/instruments/infrastructure/catalog-discovery.repository';
import type {
StockResourceDto,
StockSearchResourceDto,
import {
type StockCandlesResourceDto,
type StockCompanyResourceDto,
type StockQuoteResourceDto,
type StockResourceDto,
type StockSearchResourceDto,
} from '../src/modules/investments/instruments/stocks/presentation/stock.dto';
import type { BondSearchResourceDto } from '../src/modules/investments/instruments/bonds/presentation/bond.dto';
import { PrismaService } from '../src/prisma/prisma.service';
// HTTP + disposable SQLite: missing/partial results, exact prices, shared source reuse and revision races.
describe('local stock/bond search (SQLite HTTP)', () => {
@@ -89,8 +88,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
await delay(10);
if (
!(await prisma.tbankSyncWork.count({ where: { state: 'running' } }))
)
) {
return;
}
}
}
throw new Error('Catalog sources did not settle');
@@ -109,10 +109,11 @@ describe('local stock/bond search (SQLite HTTP)', () => {
await app.init();
await app.listen(0, '127.0.0.1');
prisma = app.get(PrismaService);
for (const id of ['owner', 'other'])
for (const id of ['owner', 'other']) {
await prisma.user.create({
data: { id, email: `${id}@example.invalid`, passwordHash: 'synthetic' },
});
}
});
beforeEach(async () => {
await prisma.tbankConnection.deleteMany();
@@ -162,7 +163,7 @@ describe('local stock/bond search (SQLite HTTP)', () => {
})),
),
);
for (const userId of ['owner', 'other'])
for (const userId of ['owner', 'other']) {
await prisma.tbankConnection.create({
data: {
userId,
@@ -173,6 +174,7 @@ describe('local stock/bond search (SQLite HTTP)', () => {
connectedAt: new Date(),
},
});
}
});
afterAll(async () => app?.close());
@@ -187,8 +189,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
expect(cold.data).toBeNull();
expect(cold.meta.availability).toBe('missing');
found.resolve([share]);
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++)
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++) {
await delay(5);
}
const partial = await read();
expect(partial.data?.[0].instrumentUid).toBe('share');
expect(partial.meta.availability).toBe('partial');
@@ -329,16 +332,18 @@ describe('local stock/bond search (SQLite HTTP)', () => {
const detail = deferred<typeof share>();
upstream.getShare.mockReturnValueOnce(detail.promise);
await read(false);
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++)
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++) {
await delay(5);
}
await get('stocks?query=OTHER', 'other').expect(200);
for (
let i = 0;
i < 100 &&
(await prisma.tbankSyncWork.count({ where: { userId: 'other' } })) < 3;
i++
)
) {
await delay(5);
}
detail.resolve({
...share,
currency: 'rub',
@@ -364,10 +369,11 @@ describe('local stock/bond search (SQLite HTTP)', () => {
const detail = deferred<typeof share>();
upstream.getShare.mockReturnValueOnce(detail.promise);
await read(false);
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++)
for (let i = 0; i < 100 && !upstream.getShare.mock.calls.length; i++) {
await delay(5);
}
await prisma.tbankConnection.delete({ where: { userId: 'owner' } });
if (action === 'replace')
if (action === 'replace') {
await prisma.tbankConnection.create({
data: {
userId: 'owner',
@@ -376,6 +382,7 @@ describe('local stock/bond search (SQLite HTTP)', () => {
connectedAt: new Date(),
},
});
}
detail.resolve(share);
await settled();
expect(
@@ -451,8 +458,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
]);
upstream.getLastPrices.mockReturnValueOnce(prices.promise);
await read(false);
for (let i = 0; i < 100 && !upstream.getLastPrices.mock.calls.length; i++)
for (let i = 0; i < 100 && !upstream.getLastPrices.mock.calls.length; i++) {
await delay(5);
}
const initiating = await prisma.tbankSyncWork.findFirstOrThrow({
where: { userId: 'owner', scopeKey: { contains: 'last-price' } },
orderBy: { createdAt: 'asc' },
@@ -575,8 +583,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
let i = 0;
i < 100 && !upstream.getAssetDescription.mock.calls.length;
i++
)
) {
await delay(5);
}
const partial = (
await get(`stocks/${uid}/company?sync=observe`).expect(200)
).body as StockCompanyResourceDto;
@@ -628,8 +637,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
const cold = (await get(`stocks/${uid}/quote`).expect(200))
.body as StockQuoteResourceDto;
expect(cold.meta.availability).toBe('missing');
for (let i = 0; i < 100 && !upstream.getLastPrices.mock.calls.length; i++)
for (let i = 0; i < 100 && !upstream.getLastPrices.mock.calls.length; i++) {
await delay(5);
}
const pending = (await get(`stocks/${uid}/quote?sync=observe`).expect(200))
.body as StockQuoteResourceDto;
expect(pending.data?.price).toBeNull();
@@ -858,8 +868,9 @@ describe('local stock/bond search (SQLite HTTP)', () => {
let i = 0;
i < 100 && !upstream.getAssetDescription.mock.calls.length;
i++
)
) {
await delay(5);
}
await prisma.tbankConnection.delete({ where: { userId: 'owner' } });
company.resolve('Late company');
await settled();