summaryrefslogtreecommitdiffstats
path: root/polygerrit-ui/app/services/scheduler/retry-scheduler_test.ts
blob: 988b167071c73560ab0b8049c492cb7977fb529f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
 * @license
 * Copyright 2022 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../../test/common-test-setup-karma.js';
import {assertFails} from '../../test/test-utils.js';
import {Scheduler} from './scheduler';
import {RetryScheduler, RetryError} from './retry-scheduler';
import {FakeScheduler} from './fake-scheduler';
import {SinonFakeTimers} from 'sinon';

suite('retry scheduler', () => {
  let clock: SinonFakeTimers;
  let fakeScheduler: FakeScheduler<number>;
  let scheduler: Scheduler<number>;
  setup(() => {
    clock = sinon.useFakeTimers();
    fakeScheduler = new FakeScheduler<number>();
    scheduler = new RetryScheduler<number>(fakeScheduler, 3, 50, 1);
  });

  async function waitForRetry(ms: number) {
    // Flush the promise so that we can reach untilTimeout
    await flush();
    // Advance the clock.
    clock.tick(ms);
    // Flush the promise that waits for the clock.
    await flush();
  }

  test('executes tasks', async () => {
    const promise = scheduler.schedule(async () => 1);
    assert.equal(fakeScheduler.scheduled.length, 1);
    fakeScheduler.resolve();
    assert.equal(fakeScheduler.scheduled.length, 0);
    const val = await promise;
    assert.equal(val, 1);
  });

  test('propagates task errors', async () => {
    const error = new Error('This is an error');
    const promise = scheduler.schedule(async () => {
      throw error;
    });
    assert.equal(fakeScheduler.scheduled.length, 1);
    assertFails(promise, error);
    fakeScheduler.resolve();
    assert.equal(fakeScheduler.scheduled.length, 0);
    await promise.catch((reason: Error) => {
      assert.equal(reason, error);
    });
  });

  test('propagates subscheduler errors', async () => {
    const error = new Error('This is an error');
    const promise = scheduler.schedule(async () => 1);
    assert.equal(fakeScheduler.scheduled.length, 1);
    assertFails(promise, error);
    fakeScheduler.reject(error);
    assert.equal(fakeScheduler.scheduled.length, 0);
    await promise.catch((reason: Error) => {
      assert.equal(reason, error);
    });
  });

  test('retries on retryable error', async () => {
    let retries = 1;
    const promise = scheduler.schedule(async () => {
      if (retries-- > 0) throw new RetryError('Retrying');
      return 1;
    });
    assert.equal(fakeScheduler.scheduled.length, 1);
    fakeScheduler.resolve();
    assert.equal(fakeScheduler.scheduled.length, 0);
    await waitForRetry(50);
    assert.equal(fakeScheduler.scheduled.length, 1);
    fakeScheduler.resolve();
    const val = await promise;
    assert.equal(val, 1);
  });

  test('retries up to 3 times', async () => {
    let retries = 3;
    const promise = scheduler.schedule(async () => {
      if (retries-- > 0) throw new RetryError('Retrying');
      return 1;
    });
    assert.equal(fakeScheduler.scheduled.length, 1);
    for (let i = 0; i < 3; i++) {
      fakeScheduler.resolve();
      assert.equal(fakeScheduler.scheduled.length, 0);
      await waitForRetry(50);
      assert.equal(fakeScheduler.scheduled.length, 1);
    }
    fakeScheduler.resolve();
    const val = await promise;
    assert.equal(val, 1);
  });

  test('fails after more than 3 times', async () => {
    let retries = 4;
    const promise = scheduler.schedule(async () => {
      if (retries-- > 0) throw new RetryError(retries, `Retrying ${retries}`);
      return 1;
    });
    assert.equal(fakeScheduler.scheduled.length, 1);
    for (let i = 0; i < 3; i++) {
      fakeScheduler.resolve();
      assert.equal(fakeScheduler.scheduled.length, 0);
      await waitForRetry(50);
      assert.equal(fakeScheduler.scheduled.length, 1);
    }
    fakeScheduler.resolve();
    assertFails(promise);
    // The error we get back should be the last error.
    await promise.catch((reason: RetryError<number>) => {
      assert.equal(reason.payload, 0);
      assert.equal(reason.message, 'Retrying 0');
    });
  });
});