@hyperlane-xyz/utils
Version:
General utilities and types for the Hyperlane network
124 lines • 4.82 kB
JavaScript
import { expect } from 'chai';
import { concurrentMap, fetchWithTimeout, pollAsync, raceWithContext, retryAsync, runWithTimeout, sleep, timeout, } from './async.js';
describe('Async Utilities', () => {
describe('sleep', () => {
it('should resolve after sleep duration', async () => {
const start = Date.now();
await sleep(100);
const duration = Date.now() - start;
expect(duration).to.be.at.least(95);
expect(duration).to.be.lessThan(200);
});
});
describe('timeout', () => {
it('should timeout a promise', async () => {
const promise = new Promise((resolve) => setTimeout(resolve, 200));
try {
await timeout(promise, 100);
throw new Error('Expected timeout error');
}
catch (error) {
expect(error.message).to.equal('Timeout reached');
}
});
});
describe('runWithTimeout', () => {
it('should run a callback with a timeout', async () => {
const result = await runWithTimeout(100, async () => {
await sleep(50);
return 'success';
});
expect(result).to.equal('success');
});
});
describe('fetchWithTimeout', () => {
it('should fetch with timeout', async () => {
// Mock fetch for testing
global.fetch = async () => {
await sleep(50);
return new Response('ok');
};
const response = await fetchWithTimeout('https://example.com', {}, 100);
expect(await response.text()).to.equal('ok');
});
});
describe('retryAsync', () => {
it('should retry async function with exponential backoff', async () => {
let attempt = 0;
const runner = async () => {
attempt++;
if (attempt < 3)
throw new Error('fail');
return 'success';
};
const result = await retryAsync(runner, 5, 10);
expect(result).to.equal('success');
});
});
describe('pollAsync', () => {
it('should poll async function until success', async () => {
let attempt = 0;
const runner = async () => {
attempt++;
if (attempt < 3)
throw new Error('fail');
return 'success';
};
const result = await pollAsync(runner, 10, 5);
expect(result).to.equal('success');
});
it('should fail after reaching max retries', async () => {
let attempt = 0;
const runner = async () => {
attempt++;
throw new Error('fail');
};
try {
await pollAsync(runner, 10, 3); // Set maxAttempts to 3
throw new Error('Expected pollAsync to throw an error');
}
catch (error) {
expect(attempt).to.equal(3); // Ensure it attempted 3 times
expect(error.message).to.equal('fail');
}
});
});
describe('raceWithContext', () => {
it('should race with context', async () => {
const promises = [
sleep(50).then(() => 'first'),
sleep(100).then(() => 'second'),
];
const result = await raceWithContext(promises);
expect(result.resolved).to.equal('first');
expect(result.index).to.equal(0);
});
});
describe('concurrentMap', () => {
it('should map concurrently with correct results', async () => {
const xs = [1, 2, 3, 4, 5, 6];
const mapFn = async (val) => {
await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work
return val * 2;
};
const result = await concurrentMap(2, xs, mapFn);
expect(result).to.deep.equal([2, 4, 6, 8, 10, 12]);
});
it('should respect concurrency limit', async () => {
const xs = [1, 2, 3, 4, 5, 6];
const concurrency = 2;
let activeTasks = 0;
let maxActiveTasks = 0;
const mapFn = async (val) => {
activeTasks++;
maxActiveTasks = Math.max(maxActiveTasks, activeTasks);
await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work
activeTasks--;
return val * 2;
};
await concurrentMap(concurrency, xs, mapFn);
expect(maxActiveTasks).to.equal(concurrency);
});
});
});
//# sourceMappingURL=async.test.js.map