refactor!: simplify runtime coordination #7
Backend verify / commits (pull_request) Successful in 29s
Backend verify / security (pull_request) Successful in 8s
Backend verify / verify (pull_request) Failing after 9m36s

Keep financial ownership, atomic publication and refresh CAS.
Failed refresh leaves cookies untouched; logout still clears them.

Remove full-history candle prefetch and special metadata retries.
Use native Pino request context instead of parallel middleware.

BREAKING CHANGE: remove investment stateVersion, queued sync state,
and tbank-sync status progress/resources. Consumers must observe
resources through sync=observe with the updated frontend contract.
This commit is contained in:
2026-10-03 11:38:08 +03:00
parent 0f27da9fd1
commit d698ed9425
48 changed files with 185 additions and 925 deletions
+10 -43
View File
@@ -41,7 +41,6 @@ describe('atomic shared candle reconciliation (SQLite)', () => {
lastSuccessfulAt: null,
resourceKey: 'detail/uid',
dataRevision: '1',
stateVersion: 'detail-v1',
sync: { id: null, state: 'idle', errorCode: null },
},
},
@@ -273,7 +272,7 @@ describe('atomic shared candle reconciliation (SQLite)', () => {
).toBe(abandoned?.work.attemptId);
await reopened.read('owner', input);
await settled();
expect(upstream.getCandles.mock.calls.length).toBeGreaterThan(1);
expect(upstream.getCandles).toHaveBeenCalledTimes(1);
expect(
(await repository.work('owner', 'revision', 'candles:uid:month'))
?.attemptId,
@@ -483,52 +482,20 @@ describe('atomic shared candle reconciliation (SQLite)', () => {
await prisma.tbankCandleCoverage.count({ where: { isComplete: false } }),
).toBe(1);
});
it('prefetches only current supported portfolio instruments and lets screen work run between history windows', async () => {
await prisma.tbankInstrument.create({
data: {
provider: 'tbank',
uid: 'opened',
instrumentType: 'share',
metadata: {},
fetchedAt: new Date(),
},
});
it('loads only the requested period and reuses its saved coverage', async () => {
const service = createService();
let release: (value: unknown[]) => void = () => {};
const blocked = new Promise<unknown[]>((resolve) => {
release = resolve;
});
upstream.getCandles.mockReturnValueOnce(blocked);
await service.prefetch('owner', 'revision', [
{ instrumentUid: 'uid', type: 'STOCK' },
{ instrumentUid: 'uid', type: 'STOCK' },
{ instrumentUid: 'not-supported', type: 'FUND' },
{ instrumentUid: null, type: 'BOND' },
]);
for (let i = 0; i < 100 && !upstream.getCandles.mock.calls.length; i++) {
await delay(5);
}
expect((upstream.getCandles.mock.calls as unknown[][])[0]?.[1]).toBe('uid');
await service.read('owner', {
uid: 'opened',
const input = {
uid: 'uid',
instrumentType: 'share',
period: 'month',
});
release([]);
} as const;
await service.read('owner', input);
await settled();
expect((upstream.getCandles.mock.calls as unknown[][])[1]?.[1]).toBe(
'opened',
);
expect(
new Set(upstream.getCandles.mock.calls.map((call: unknown[]) => call[1])),
).toEqual(new Set(['opened', 'uid']));
expect(upstream.getCandles).toHaveBeenCalledTimes(1);
expect(await repository.work('owner', 'revision', 'candles:uid:all')).toBeNull();
const count = upstream.getCandles.mock.calls.length;
await service.prefetch('owner', 'revision', [
{ instrumentUid: 'uid', type: 'STOCK' },
]);
await service.read('owner', input);
await settled();
expect(upstream.getCandles).toHaveBeenCalledTimes(count);
expect(
(await repository.work('owner', 'revision', 'candles:opened:all'))?.state,
).toBe('complete');
});
});