test: align resource availability metadata
Backend verify / commits (pull_request) Successful in 34s
Backend verify / security (pull_request) Successful in 13s
Backend verify / verify (pull_request) Successful in 5m38s

This commit is contained in:
2026-10-09 22:21:22 +03:00
parent 20fb2f6c6e
commit 34f866fcec
9 changed files with 62 additions and 39 deletions
@@ -194,6 +194,7 @@ describe('persistent portfolio projection', () => {
const partial = await service.read('user', 'all');
expect(partial.data).toBeNull();
expect(partial.meta.availability).toBe('temporarily-unavailable');
expect(partial.meta.completeness).toBe('incomplete');
const saved = await service.saved('user', 'all');
expect(saved.accounts[0].snapshot?.totalsByCurrency).toEqual(
snapshot.totalsByCurrency,
@@ -32,6 +32,7 @@ import { mapPortfolio } from '../infrastructure/tbank-portfolio.mapper';
import { PortfolioDto } from '../presentation/portfolio.dto';
const MISSING_DATA_REVISION = 0;
const NO_ACCOUNTS = 0;
const membershipFreshForMs = tbankSourceTtlMs.membership;
@@ -113,7 +114,11 @@ export class PortfolioService {
const { hourly, ...saved } = await this.saved(userId, accountId);
const missing =
saved.membership.completeness === 'incomplete' ||
saved.accounts.some((account) => !account.snapshot);
(saved.accounts.length > NO_ACCOUNTS && saved.accounts.some((account) => !account.snapshot));
const available =
!missing &&
(saved.membership.availability === 'available' ||
saved.accounts.some((account) => account.snapshot));
const metadata = await this.metadata.savedMany(
saved.accounts.flatMap((account) => account.snapshot?.positions ?? []),
);
@@ -162,6 +167,11 @@ export class PortfolioService {
]
: [],
);
let availability: 'available' | 'complete' | 'missing' | 'partial';
if (available) availability = 'available';
else if (!missing) availability = 'complete';
else if (portfolios.length > NO_ACCOUNTS) availability = 'partial';
else availability = 'missing';
const lastSuccessfulAt =
// eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings.
portfolios.map((portfolio) => portfolio.lastSuccessfulAt).sort()[0] ??
@@ -205,11 +215,7 @@ export class PortfolioService {
]),
metadataDependencies.map((dependency) => dependency.dataRevision),
]),
availability: missing
? portfolios.length
? 'partial'
: 'missing'
: 'complete',
availability,
fetchedAt: lastSuccessfulAt ? new Date(lastSuccessfulAt) : null,
freshForMs: portfolios.length
? portfolioFreshForMs