Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ jobs:
target_file: 'packages/shared/common/dist/esm/index.mjs'
package_name: '@launchdarkly/js-sdk-common'
pr_number: ${{ github.event.number }}
size_limit: 29000
size_limit: 29500
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
createAfterConsecutiveSuccesses,
createAfterHealthyFor,
} from '../../../src/datasource/retry/ResetPolicy';

const MINUTE = 60 * 1000;

let now = 0;
const clock = (): number => now;

beforeEach(() => {
now = 0;
});

it('is not satisfied before any healthy report', () => {
const policy = createAfterHealthyFor(MINUTE, clock);
now = 10 * MINUTE;
expect(policy.isSatisfied()).toEqual(false);
});

it('is satisfied once the healthy stretch reaches the threshold, and not a moment sooner', () => {
const policy = createAfterHealthyFor(MINUTE, clock);
policy.noteHealthy();
now = MINUTE - 1;
expect(policy.isSatisfied()).toEqual(false);
now = MINUTE;
expect(policy.isSatisfied()).toEqual(true);
});

it('does not move the start of the stretch on repeated healthy reports', () => {
const policy = createAfterHealthyFor(MINUTE, clock);
policy.noteHealthy();
for (let i = 1; i < 60; i += 1) {
now = i * 1000;
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(false);
}
now = MINUTE;
expect(policy.isSatisfied()).toEqual(true);
});

it('clears the healthy stretch on a failure', () => {
const policy = createAfterHealthyFor(MINUTE, clock);
policy.noteHealthy();
now = 30 * 1000;
policy.noteFailure();
now = 15 * MINUTE;
expect(policy.isSatisfied()).toEqual(false);
});

it('is satisfied by consecutive successes and not by fewer', () => {
const policy = createAfterConsecutiveSuccesses(2);
expect(policy.isSatisfied()).toEqual(false);
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(false);
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(true);
});

it('starts the success count over on a failure', () => {
const policy = createAfterConsecutiveSuccesses(2);
policy.noteHealthy();
policy.noteFailure();
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(false);
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(true);
});

it('measures the healthy stretch with the default clock', () => {
const policy = createAfterHealthyFor(60 * MINUTE);
expect(policy.isSatisfied()).toEqual(false);
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(false);
});

it('falls back to the wall clock when no monotonic source exists', () => {
const savedPerformance = (globalThis as any).performance;
const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(0);
delete (globalThis as any).performance;
try {
const policy = createAfterHealthyFor(MINUTE);
policy.noteHealthy();
expect(policy.isSatisfied()).toEqual(false);
nowSpy.mockReturnValue(MINUTE);
expect(policy.isSatisfied()).toEqual(true);
} finally {
nowSpy.mockRestore();
(globalThis as any).performance = savedPerformance;
}
});
Loading
Loading