feat: add hourly portfolio sync history
Backend verify / commits (pull_request) Successful in 33s
Backend verify / security (pull_request) Failing after 12s
Backend verify / verify (pull_request) Skipped

This commit is contained in:
2026-10-09 09:04:41 +03:00
parent 03e75f8d2b
commit 0057925888
49 changed files with 1674 additions and 1870 deletions
+10 -11
View File
@@ -204,27 +204,28 @@ describe('durable shared instrument metadata (SQLite)', () => {
await settled();
expect((await service.read('other', identity, true))?.data).toEqual(known);
});
it('keeps last good metadata and source time after refresh failure; stale observation does not restart work', async () => {
it('keeps last good metadata and source time after refresh failure; observation does not restart work', async () => {
await service.read('owner', identity);
await settled();
const old = new Date(Date.now() - 86_400_000);
const old = new Date(Date.now() - 10 * 86_400_000);
await prisma.tbankInstrumentLookup.updateMany({ data: { fetchedAt: old } });
await prisma.tbankInstrument.updateMany({ data: { fetchedAt: old } });
const calls = upstream.getInstrumentByUid.mock.calls.length;
expect((await service.read('owner', identity, true))?.fresh).toBe(false);
expect((await service.read('owner', identity, true))?.fresh).toBe(true);
expect(upstream.getInstrumentByUid).toHaveBeenCalledTimes(calls);
const failedHandle = await service.request('owner', 'owner-revision', identity, true);
upstream.getInstrumentByUid.mockRejectedValueOnce(
new Error('upstream unavailable'),
);
upstream.getInstrumentByFigi.mockRejectedValueOnce(
new Error('upstream unavailable'),
);
await service.read('owner', identity);
await settled();
const failed = await service.read('owner', identity, true);
expect(failed?.data).toEqual(known);
expect(failed?.fetchedAt).toEqual(old);
expect(failed?.work?.state).toBe('failed');
if (!failedHandle) throw new Error('Expected forced refresh work');
expect((await prisma.tbankSyncWork.findUniqueOrThrow({ where: { id: failedHandle } })).state).toBe('failed');
await delay(20);
expect(upstream.getInstrumentByUid).toHaveBeenCalledTimes(calls + 1);
});
@@ -252,20 +253,18 @@ describe('durable shared instrument metadata (SQLite)', () => {
expect(after?.work?.state).toBe('failed');
expect(upstream.getInstrumentByUid).toHaveBeenCalledTimes(2);
});
it('uses the exact daily TTL boundary and does not count another instrument as covered', async () => {
it('keeps saved metadata fresh and does not count another instrument as covered', async () => {
await service.read('owner', identity);
await settled();
const timestamp = new Date('2026-09-01Z');
await prisma.tbankInstrumentLookup.updateMany({
data: { fetchedAt: timestamp },
});
const clock = jest
.spyOn(Date, 'now')
.mockReturnValue(timestamp.valueOf() + 86_400_000 - 1);
const clock = jest.spyOn(Date, 'now').mockReturnValue(timestamp.valueOf());
try {
expect((await service.read('owner', identity, true))?.fresh).toBe(true);
clock.mockReturnValue(timestamp.valueOf() + 86_400_000);
expect((await service.read('owner', identity, true))?.fresh).toBe(false);
clock.mockReturnValue(timestamp.valueOf() + 10 * 86_400_000);
expect((await service.read('owner', identity, true))?.fresh).toBe(true);
expect(
(await service.read('owner', { instrumentUid: 'other-uid' }, true))
?.complete,