refactor!: support RUB only (WealthLedger/main#55)

Filter non-RUB data at ingest, collapse per-currency collections into single RUB Money, drop currency from TbankPortfolioValuePoint, remove NON_RUB_ACTIVITY and screener currency filter.

BREAKING CHANGE: per-currency API fields replaced with single RUB Money; stock screener currency param removed.
This commit is contained in:
2026-11-11 08:22:16 +03:00
parent fee61537b6
commit 3b55b611b5
60 changed files with 624 additions and 736 deletions
+21 -25
View File
@@ -80,7 +80,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
],
});
upstream.getPositions.mockResolvedValue({
money: [{ units: '5', currency: 'usd' }],
money: [{ units: '5', currency: 'rub' }],
});
});
@@ -123,7 +123,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
}
describe('1.1 Point persistence', () => {
it('writes points for fully valued snapshot with two currencies', async () => {
it('writes a single RUB point for a fully valued snapshot', async () => {
await createUser('owner');
await putConnection('owner', 'token-a').expect(200);
await publishPortfolio('owner', 'a');
@@ -133,12 +133,10 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
include: { points: true },
});
expect(history?.deletedAt).toBeNull();
expect(history?.points).toHaveLength(2);
expect(history?.points).toHaveLength(1);
const rubPoint = history?.points.find((p) => p.currency === 'RUB');
const usdPoint = history?.points.find((p) => p.currency === 'USD');
expect(rubPoint?.value).toBe('21');
expect(usdPoint?.value).toBe('5');
const rubPoint = history?.points[0];
expect(rubPoint?.value).toBe('26');
const snapshot = await prisma.tbankPortfolio.findUniqueOrThrow({
where: {
@@ -201,7 +199,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
expect(points).toHaveLength(0);
});
it('writes no points for empty account without currency', async () => {
it('writes a zero point for an empty fully valued account', async () => {
upstream.getPortfolio.mockResolvedValue({ positions: [] });
upstream.getPositions.mockResolvedValue({ money: [] });
@@ -212,7 +210,8 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const points = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(points).toHaveLength(0);
expect(points).toHaveLength(1);
expect(points[0].value).toBe('0');
});
it('deduplicates repeated publication of same attempt', async () => {
@@ -258,7 +257,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const points = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(points).toHaveLength(2);
expect(points).toHaveLength(1);
});
it('late attempt after token replacement adds no points', async () => {
@@ -282,10 +281,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const published = {
lastSuccessfulAt: new Date().toISOString(),
isStale: false,
totalsByCurrency: [
{ currency: 'RUB', value: '100' },
{ currency: 'USD', value: '50' },
],
totals: { currency: 'RUB', value: '100' },
positions: [
{
figi: 'stock',
@@ -302,8 +298,8 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
issuer: null,
},
],
cashBalances: [{ currency: 'USD', value: '50' }],
structure: { currencies: [], unvaluedCategories: [] },
cash: { currency: 'RUB', value: '0' },
structure: { totalValue: '100', hasNegativeValues: false, categories: [], unvaluedCategories: [] },
};
const result = await app.get(PortfolioService)['repository'].publish(oldWork, 'a', published);
@@ -323,7 +319,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const pointsBefore = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(pointsBefore).toHaveLength(2);
expect(pointsBefore).toHaveLength(1);
upstream.getPortfolio.mockRejectedValue(new Error('Upstream failed'));
await publishPortfolio('owner', 'a');
@@ -331,7 +327,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const pointsAfter = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(pointsAfter).toHaveLength(2);
expect(pointsAfter).toHaveLength(1);
expect(pointsAfter).toEqual(pointsBefore);
});
});
@@ -345,7 +341,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const oldPoints = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(oldPoints).toHaveLength(2);
expect(oldPoints).toHaveLength(1);
upstream.validateToken.mockResolvedValue([accounts[0]]);
await putConnection('owner', 'token-b').expect(200);
@@ -361,8 +357,8 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
where: { userId: 'owner', accountId: 'a' },
orderBy: { createdAt: 'asc' },
});
expect(newPoints.length).toBeGreaterThan(2);
expect(newPoints.slice(0, 2)).toEqual(oldPoints);
expect(newPoints.length).toBeGreaterThan(1);
expect(newPoints.slice(0, 1)).toEqual(oldPoints);
});
it('hides history when account disappears from membership', async () => {
@@ -392,7 +388,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const pointsB = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'b' },
});
expect(pointsB).toHaveLength(2);
expect(pointsB).toHaveLength(1);
});
it('leaves visibility unchanged on membership fetch error', async () => {
@@ -424,7 +420,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const points = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(points).toHaveLength(2);
expect(points).toHaveLength(1);
await request(app.getHttpServer())
.delete('/api/v1/tbank-connection')
@@ -450,7 +446,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const pointsAfter = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(pointsAfter.length).toBeGreaterThan(2);
expect(pointsAfter.length).toBeGreaterThan(1);
});
it('unhides when disappeared account returns in membership', async () => {
@@ -530,7 +526,7 @@ describe('T-Bank value history persistence and visibility (SQLite)', () => {
const points = await prisma.tbankPortfolioValuePoint.findMany({
where: { userId: 'owner', accountId: 'a' },
});
expect(points).toHaveLength(2);
expect(points).toHaveLength(1);
});
});
});