chore: strengthen backend eslint profile

This commit is contained in:
2026-09-28 16:37:01 +03:00
committed by ksv741
parent 53b2794963
commit a970eaaf7a
179 changed files with 4485 additions and 2716 deletions
@@ -22,10 +22,10 @@ export function bankBalance(
cardId: string,
opening: bigint,
operations: Array<{
kind: string;
cardId: string;
targetCardId: string | null;
amountKopecks: bigint;
cardId: string;
kind: string;
targetCardId: null | string;
}>,
): bigint {
// ponytail: O(N) history fold; optimize only after measuring growing histories.
@@ -35,19 +35,21 @@ export function bankBalance(
if (
operation.kind === BankOperationKind.Expense ||
operation.kind === BankOperationKind.Transfer
)
) {
balance -= operation.amountKopecks;
else if (
} else if (
operation.kind === BankOperationKind.Topup ||
operation.kind === BankOperationKind.Refund
)
) {
balance += operation.amountKopecks;
}
}
if (
operation.kind === BankOperationKind.Transfer &&
operation.targetCardId === cardId
)
) {
balance += operation.amountKopecks;
}
}
return balance;
}
@@ -55,17 +57,17 @@ export function bankBalance(
export function bankSummary(
period: { from: string; to: string },
operations: Array<{
kind: string;
date: string;
amountKopecks: bigint;
categoryId: string | null;
categoryId: null | string;
date: string;
kind: string;
}>,
) {
let expenses = 0n;
let refunds = 0n;
const categories = new Map<
string,
{ categoryId: string; expenses: bigint; refunds: bigint; net: bigint }
{ categoryId: string; expenses: bigint; net: bigint; refunds: bigint }
>();
// ponytail: O(N) history fold; optimize only after measuring growing histories.
for (const operation of operations) {
@@ -73,16 +75,17 @@ export function bankSummary(
if (
operation.kind !== BankOperationKind.Expense &&
operation.kind !== BankOperationKind.Refund
)
) {
continue;
}
const expense =
operation.kind === BankOperationKind.Expense
? operation.amountKopecks
: 0n;
operation.kind === BankOperationKind.Expense ?
operation.amountKopecks :
0n;
const refund =
operation.kind === BankOperationKind.Refund
? operation.amountKopecks
: 0n;
operation.kind === BankOperationKind.Refund ?
operation.amountKopecks :
0n;
expenses += expense;
refunds += refund;
if (operation.categoryId !== null) {
@@ -39,8 +39,9 @@ describe('RUB money', () => {
});
it('requires positive operation amounts, including at the maximum', () => {
for (const value of ['0', '-0.00', '-0.01'])
for (const value of ['0', '-0.00', '-0.01']) {
expect(() => parseRubMoney({ currency: 'RUB', value }, true)).toThrow();
}
expect(parseRubMoney({ currency: 'RUB', value: '0.01' }, true)).toBe(1n);
expect(
parseRubMoney({ currency: 'RUB', value: '92233720368547758.07' }, true),
@@ -1,6 +1,7 @@
import { decimalFromString, formatDecimal } from '../../../../common/decimal';
import type { Money } from '../../../../common/money.types';
import { decimalFromString, formatDecimal } from '../../../../common/decimal';
const MIN_KOPECKS = -9_223_372_036_854_775_808n;
const MAX_KOPECKS = 9_223_372_036_854_775_807n;
@@ -9,16 +10,20 @@ export function parseRubMoney(money: Money, positive = false): bigint {
money?.currency !== 'RUB' ||
typeof money.value !== 'string' ||
money.value !== money.value.trim()
)
) {
throw new TypeError('Expected RUB money with a decimal string');
}
const amount = decimalFromString(money.value);
if (amount.scale > 2)
if (amount.scale > 2) {
throw new RangeError('RUB money supports at most two decimal places');
}
const kopecks = amount.value * 10n ** BigInt(2 - amount.scale);
if (kopecks < MIN_KOPECKS || kopecks > MAX_KOPECKS)
if (kopecks < MIN_KOPECKS || kopecks > MAX_KOPECKS) {
throw new RangeError('RUB money is outside the signed 64-bit range');
if (positive && kopecks <= 0n)
}
if (positive && kopecks <= 0n) {
throw new RangeError('Operation amount must be positive');
}
return kopecks;
}