refactor!: simplify runtime coordination #7
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:
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user