Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,22 @@ process.on('uncaughtException', (error) => {
logger.error('Uncaught Exception:', error);
process.exit(1);
});

const server = app.listen(port, () => {
logger.info(`Server running on port ${port}`);
});

const shutdown = (signal: string) => {
logger.info(`${signal} received, shutting down gracefully`);
server.close(() => {
logger.info('Server closed, all in-flight requests completed');
process.exit(0);
});
setTimeout(() => {
logger.error('Graceful shutdown timed out, forcing exit');
process.exit(1);
}, 10000);
};

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
65 changes: 65 additions & 0 deletions oracle/tests/contract-updater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,71 @@ describe('ContractUpdater', () => {
expect(results[1].asset).toBe('BTC');
});

describe('retry backoff delay verification', () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

it('should apply exponential backoff delays between retries', async () => {
const delays: number[] = [];
let callCount = 0;

const mockFn = vi.fn().mockImplementation(() => {
callCount++;
if (callCount < 3) throw new Error('Temporary failure');
return Promise.resolve({ success: true });
});

const promise = updater.updateWithRetry(mockFn, {
maxRetries: 3,
baseDelay: 1000,
backoffFactor: 2,
});

// Advance through first delay (1s)
await vi.advanceTimersByTimeAsync(1000);
delays.push(1000);

// Advance through second delay (2s)
await vi.advanceTimersByTimeAsync(2000);
delays.push(2000);

await promise;

expect(delays[0]).toBe(1000); // 1s
expect(delays[1]).toBe(2000); // 2s
expect(mockFn).toHaveBeenCalledTimes(3);
});

it('should apply 4s delay on third retry', async () => {
let callCount = 0;

const mockFn = vi.fn().mockImplementation(() => {
callCount++;
if (callCount < 4) throw new Error('Temporary failure');
return Promise.resolve({ success: true });
});

const promise = updater.updateWithRetry(mockFn, {
maxRetries: 4,
baseDelay: 1000,
backoffFactor: 2,
});

await vi.advanceTimersByTimeAsync(1000); // retry 1
await vi.advanceTimersByTimeAsync(2000); // retry 2
await vi.advanceTimersByTimeAsync(4000); // retry 3

await promise;

expect(mockFn).toHaveBeenCalledTimes(4);
});
});

it('should handle empty price array', async () => {
const results = await updater.updatePrices([]);

Expand Down