Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Zorkaltsev committed May 11, 2024
1 parent 2d1960e commit 7d73ba9
Showing 1 changed file with 111 additions and 39 deletions.
150 changes: 111 additions & 39 deletions src/__tests__/unit/retryer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('retryer', () => {

const ONLY_TEST = '';

const MAX_ATTEMPTS = 4;
const MAX_TEST_ATTEMPTS = 4;
const testRetryParameters = new RetryParameters({
timeout: 10_000,
});
Expand Down Expand Up @@ -121,10 +121,10 @@ describe('retryer', () => {
it(testName, async () => {
const {ctx} = Context.createNew();
// @ts-ignore
let res: number, err: any, attemptsCount = 1;
let res: number, err: any;
/* Note: has .then() at the end */
const awaitRes = new RetryStrategy(testRetryParameters, testLogger)
.retry(ctx, async () => {
.retry(ctx, async (_ctx, _logger, attemptsCount) => {
const err = new Error('test');
if (backoff !== null)
(err as any)[RetryPolicySymbol] = {
Expand All @@ -133,7 +133,7 @@ describe('retryer', () => {
idempotent,
}
if (simpleError === undefined) { // success after errors
if (attemptsCount + 1 === MAX_ATTEMPTS) {
if (attemptsCount + 1 === MAX_TEST_ATTEMPTS) {
return {result: 12};
} else { // before success result should preceed few errors
return {err, idempotent: isIdempotentOp};
Expand All @@ -149,31 +149,7 @@ describe('retryer', () => {
.catch((_err) => {
err = _err;
});
// run retries with fake timer till the end successful or MAX_ATTEMPTS
let logLineNumber = 0;
main: while (true) {
// gives up time in the event loop to perform a next retryer step
await new Promise((resolve) => {
notFakeSetTimeout(resolve, 0);
});
// read rest of the log
readLog: for (; logLineNumber < testLoggerFn.mock.calls.length;) {
const logLine = testLoggerFn.mock.calls[logLineNumber++];
if (logLine?.[0] !== 'debug') continue; // skip
switch (logLine[1]) {
case immediateBackoffRetryMessage:
case fastBackoffRetryMessage:
case slowBackoffRetryMessage:
await fakeTimersFixture.advanceTimer(logLine[3]);
// stop reading the log by count of attempts
if (++attemptsCount === MAX_ATTEMPTS) break main;
break readLog;
case notRetryableErrorMessage:
case successAfterNAttempts:
break main;
}
}
}
await runRetryerWithLogReading(MAX_TEST_ATTEMPTS);
let expectedLog = [];
if (backoff === null || (isIdempotentOp ? !idempotent : !nonIdempotent)) {
expectedLog.push([
Expand All @@ -184,7 +160,7 @@ describe('retryer', () => {
} else {
switch (backoff) {
case Backoff.No: {
for (let i = 0; i < (simpleError === undefined ? (MAX_ATTEMPTS - 1) : MAX_ATTEMPTS); i++) {
for (let i = 0; i < (simpleError === undefined ? (MAX_TEST_ATTEMPTS - 1) : MAX_TEST_ATTEMPTS); i++) {
expectedLog.push([
'debug',
immediateBackoffRetryMessage,
Expand All @@ -195,7 +171,7 @@ describe('retryer', () => {
break;
}
case Backoff.Fast: {
for (let i = 0; i < (simpleError === undefined ? (MAX_ATTEMPTS - 1) : MAX_ATTEMPTS); i++) {
for (let i = 0; i < (simpleError === undefined ? (MAX_TEST_ATTEMPTS - 1) : MAX_TEST_ATTEMPTS); i++) {
expectedLog.push([
'debug',
fastBackoffRetryMessage,
Expand All @@ -206,7 +182,7 @@ describe('retryer', () => {
break;
}
case Backoff.Slow: {
for (let i = 0; i < (simpleError === undefined ? (MAX_ATTEMPTS - 1) : MAX_ATTEMPTS); i++) {
for (let i = 0; i < (simpleError === undefined ? (MAX_TEST_ATTEMPTS - 1) : MAX_TEST_ATTEMPTS); i++) {
expectedLog.push([
'debug',
slowBackoffRetryMessage,
Expand All @@ -223,26 +199,122 @@ describe('retryer', () => {
expectedLog.push([
'debug',
successAfterNAttempts,
MAX_ATTEMPTS,
MAX_TEST_ATTEMPTS,
]);
}
expect(testLoggerFn.mock.calls).toEqual(expectedLog);
});
}

// it('drop counter on another error', async () => {
//
// });
it('drop error counter on another error', async () => {
class Error1 extends Error {
[RetryPolicySymbol] = {backoff: Backoff.Slow, nonIdempotent: true, idempotent: true};
}

class Error2 extends Error {
[RetryPolicySymbol] = {backoff: Backoff.Slow, nonIdempotent: true, idempotent: true};
}

const {ctx} = Context.createNew();
const awaitRes = new RetryStrategy(testRetryParameters, testLogger)
.retry(ctx, async (_ctx, _logger, attemptsCount) => {
if (attemptsCount <= 3) return {err: new Error1('test1'), idempotent: true};
if (attemptsCount <= 5) return {err: new Error2('test2'), idempotent: true};
return {result: 12};
})
.catch((err) => {
expect(err).not.toBeDefined();
});
await runRetryerWithLogReading();
expect(await awaitRes).toBe(12);
expect(testLoggerFn.mock.calls).toEqual([
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error1('test1'),
65,
],
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error1('test1'),
130,
],
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error1('test1'),
260,
],
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error1('test1'),
260,
],
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error2('test2'),
65,
],
[
"debug",
"Caught an error %s, retrying with slow backoff in %d ms",
new Error2('test2'),
130,
],
[
"debug",
"The operation completed successfully after %d attempts",
7,
],
]);
});

// it('stop on context cancel', async () => {
//
// });
// it('stop on context timeout', async () => {
//
// });

it('stop on context timeout', async () => {

});
it('stop on parameters timeout', async () => {

});
// it('stop on context done', async () => {
//
// });
// it('limit by count for legacy', async () => {
//
// });

async function runRetryerWithLogReading(maxTestAttempts?: number) {
// run retries with fake timer till the end successful or MAX_ATTEMPTS
let logLineNumber = 0, testAttemptsCount = 1;
main: while (true) {
// gives up time in the event loop to perform a next retryer step
await new Promise((resolve) => {
notFakeSetTimeout(resolve, 0);
});
// read rest of the log
readLog: for (; logLineNumber < testLoggerFn.mock.calls.length;) {
const logLine = testLoggerFn.mock.calls[logLineNumber++];
if (logLine?.[0] !== 'debug') continue; // skip
switch (logLine[1]) {
case immediateBackoffRetryMessage:
case fastBackoffRetryMessage:
case slowBackoffRetryMessage:
await fakeTimersFixture.advanceTimer(logLine[3]);
// stop reading the log by count of attempts
if (maxTestAttempts !== undefined
&& ++testAttemptsCount === maxTestAttempts) break main;
break readLog;
case notRetryableErrorMessage:
case successAfterNAttempts:
break main;
}
}
}
}
})

0 comments on commit 7d73ba9

Please sign in to comment.