From 70bad3f95749231868dc725f0e1e67dfcf89eb9d Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 26 Feb 2024 17:18:45 -0800 Subject: [PATCH] reuse test setup code --- .../test/local/flagConfigStreamer.test.ts | 768 +++++------------- 1 file changed, 195 insertions(+), 573 deletions(-) diff --git a/packages/node/test/local/flagConfigStreamer.test.ts b/packages/node/test/local/flagConfigStreamer.test.ts index 1ddfe9d..4f0eaf9 100644 --- a/packages/node/test/local/flagConfigStreamer.test.ts +++ b/packages/node/test/local/flagConfigStreamer.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import assert from 'assert'; import { InMemoryFlagConfigCache } from 'src/index'; @@ -8,40 +7,40 @@ import { FlagConfigStreamer } from 'src/local/streamer'; import { MockHttpClient } from './util/mockHttpClient'; import { getNewClient } from './util/mockStreamEventSource'; -const apiKey = 'client-xxxx'; -const serverUrl = 'http://localhostxxxx:799999999'; -const streamConnTimeoutMillis = 1000; -const streamFlagConnTimeoutMillis = 1000; -const streamFlagTryAttempts = 2; -const streamFlagTryDelayMillis = 1000; -const retryStreamFlagDelayMillis = 15000; - -// Following values may not be used in all tests. -const pollingIntervalMillis = 1000; -const fetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - return { status: 500, body: undefined }; - }), -); -const cache = new InMemoryFlagConfigCache(); - -test('FlagConfigUpdater.connect, success', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( +const getTestObjs = ({ + pollingIntervalMillis = 1000, + streamConnTimeoutMillis = 1000, + streamFlagConnTimeoutMillis = 1000, + streamFlagTryAttempts = 2, + streamFlagTryDelayMillis = 1000, + retryStreamFlagDelayMillis = 15000, + apiKey = 'client-xxxx', + serverUrl = 'http://localhostxxxx:799999999', +}) => { + const fetchObj = { fetchCalls: 0, fetcher: undefined }; + let dataI = 0; + const data = [ + '[{"key": "fetcher-a", "variants": {}, "segments": []}]', + '[{"key": "fetcher-b", "variants": {}, "segments": []}]', + ]; + fetchObj.fetcher = new FlagConfigFetcher( apiKey, new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; + fetchObj.fetchCalls++; + return { status: 200, body: data[dataI] }; }), ); + const fetcherReturnNext = () => { + dataI++; + }; + const cache = new InMemoryFlagConfigCache(); + const mockClient = getNewClient(); const updater = new FlagConfigStreamer( apiKey, - mockFetcher, + fetchObj.fetcher, cache, mockClient.clientClass, - 100, + pollingIntervalMillis, streamConnTimeoutMillis, streamFlagConnTimeoutMillis, streamFlagTryAttempts, @@ -50,11 +49,23 @@ test('FlagConfigUpdater.connect, success', async () => { serverUrl, false, ); + return { + fetchObj, + fetcherReturnNext, + cache, + mockClient, + updater, + }; +}; + +test('FlagConfigUpdater.connect, success', async () => { + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); assert(mockClient.numCreated == 1); await updater.stop(); // Pass @@ -65,42 +76,20 @@ test('FlagConfigUpdater.connect, success', async () => { }); test('FlagConfigUpdater.connect, start success, gets initial flag configs, gets subsequent flag configs', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const cache = new InMemoryFlagConfigCache(); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[{"key": "a", "variants": {}, "segments": []}]', }); - assert(fetchCalls == 0); + assert(fetchObj.fetchCalls == 0); assert(mockClient.numCreated == 1); await new Promise((r) => setTimeout(r, 200)); assert((await cache.get('a')).key == 'a'); - await mockClient.client!.doMsg({ + await mockClient.client.doMsg({ data: '[{"key": "b", "variants": {}, "segments": []}]', }); await new Promise((r) => setTimeout(r, 200)); @@ -116,47 +105,20 @@ test('FlagConfigUpdater.connect, start success, gets initial flag configs, gets }); test('FlagConfigUpdater.connect, stream start fail, fallback to poller, poller updates flag configs correctly', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - let dataI = 0; - const data = [ - '[{"key": "a", "variants": {}, "segments": []}]', - '[{"key": "b", "variants": {}, "segments": []}]', - ]; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: data[dataI] }; - }), - ); - const cache = new InMemoryFlagConfigCache(); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doErr({ status: 501 }); // Send 501 fatal err to fallback to poller. + await mockClient.client.doErr({ status: 501 }); // Send 501 fatal err to fallback to poller. await new Promise((r) => setTimeout(r, 200)); // Wait for poller to poll. - assert(fetchCalls >= 1); + assert(fetchObj.fetchCalls >= 1); assert(mockClient.numCreated == 1); - assert((await cache.get('a')).key == 'a'); + assert((await cache.get('fetcher-a')).key == 'fetcher-a'); - dataI++; + fetcherReturnNext(); await new Promise((r) => setTimeout(r, 200)); // Wait for poller to poll. - assert((await cache.get('b')).key == 'b'); - assert((await cache.get('a')) == undefined); + assert((await cache.get('fetcher-b')).key == 'fetcher-b'); + assert((await cache.get('fetcher-a')) == undefined); await updater.stop(); // Pass @@ -167,45 +129,24 @@ test('FlagConfigUpdater.connect, stream start fail, fallback to poller, poller u }); test('FlagConfigUpdater.connect, start success, gets error initial flag configs, fallback to poller', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: 'xxx', }); // Initial error flag configs for first try. await new Promise((r) => setTimeout(r, 1000)); // Need to yield quite some time to start retry. - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[{"key: aaa}]', }); // Another error flag configs for second try. await new Promise((r) => setTimeout(r, 1000)); // Need to yield quite some time to start retry. // Should fallbacked to poller. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); assert(mockClient.numCreated == 2); await updater.stop(); @@ -217,60 +158,38 @@ test('FlagConfigUpdater.connect, start success, gets error initial flag configs, }); test('FlagConfigUpdater.connect, start success, gets ok initial flag configs, but gets error flag configs later, fallback to poller', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const cache = new InMemoryFlagConfigCache(); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[{"key": "a", "variants": {}, "segments": []}]', }); // Initial flag configs are fine. await new Promise((r) => setTimeout(r, 200)); - assert(fetchCalls == 0); + assert(fetchObj.fetchCalls == 0); let n = mockClient.numCreated; assert(n == 1); // Start error ones. - await mockClient.client!.doMsg({ + await mockClient.client.doMsg({ data: 'hahaha', }); // An error flag configs to start retry. await new Promise((r) => setTimeout(r, 500)); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: 'xxx', }); // Error flag configs for first retry. await new Promise((r) => setTimeout(r, 1000)); // Need to yield quite some time to start retry. - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[{"key: aaa}]', }); // Error flag configs for second retry. await new Promise((r) => setTimeout(r, 1000)); // Need to yield quite some time to start retry. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); n = mockClient.numCreated; assert(n == 3); @@ -283,36 +202,15 @@ test('FlagConfigUpdater.connect, start success, gets ok initial flag configs, bu }); test('FlagConfigUpdater.connect, open but no initial flag configs', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); + await mockClient.client.doOpen({ type: 'open' }); await new Promise((r) => setTimeout(r, 1100)); - await mockClient.client!.doOpen({ type: 'open' }); + await mockClient.client.doOpen({ type: 'open' }); await new Promise((r) => setTimeout(r, 2000)); - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); assert(mockClient.numCreated == 2); await updater.stop(); // Pass @@ -323,38 +221,17 @@ test('FlagConfigUpdater.connect, open but no initial flag configs', async () => }); test('FlagConfigUpdater.connect, success and then fails and then reconnects', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - await mockClient.client!.doErr({ status: 500 }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + await mockClient.client.doErr({ status: 500 }); await new Promise((r) => setTimeout(r, 500)); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); assert(mockClient.numCreated == 2); await updater.stop(); // Pass @@ -365,26 +242,13 @@ test('FlagConfigUpdater.connect, success and then fails and then reconnects', as }); test('FlagConfigUpdater.connect, timeout first try, retry success', async () => { - const mockClient = getNewClient(); - const updater = new FlagConfigStreamer( - apiKey, - fetcher, - cache, - mockClient.clientClass, - pollingIntervalMillis, - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({}); try { updater.start(); await new Promise((r) => setTimeout(r, 2200)); // Wait at least 2 secs, at most 3 secs for first try timeout. - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); assert(mockClient.numCreated == 2); await updater.stop(); // Pass @@ -395,32 +259,11 @@ test('FlagConfigUpdater.connect, timeout first try, retry success', async () => }); test('FlagConfigUpdater.connect, retry timeout, backoff to poll after 2 tries', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { await updater.start(); // Awaits start(), no data sent. - assert(fetchCalls >= 1); + assert(fetchObj.fetchCalls >= 1); assert(mockClient.numCreated == 2); await updater.stop(); // Pass @@ -431,34 +274,13 @@ test('FlagConfigUpdater.connect, retry timeout, backoff to poll after 2 tries', }); test('FlagConfigUpdater.connect, 501, backoff to poll after 1 try', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doErr({ status: 501 }); // Send 501 fatal err. + await mockClient.client.doErr({ status: 501 }); // Send 501 fatal err. await new Promise((r) => setTimeout(r, 200)); // Wait for poller to poll. - assert(fetchCalls >= 1); + assert(fetchObj.fetchCalls >= 1); assert(mockClient.numCreated == 1); await updater.stop(); // Pass @@ -469,36 +291,15 @@ test('FlagConfigUpdater.connect, 501, backoff to poll after 1 try', async () => }); test('FlagConfigUpdater.connect, 404, backoff to poll after 2 tries', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); - await mockClient.client!.doErr({ status: 404 }); // Send error for first try. + await mockClient.client.doErr({ status: 404 }); // Send error for first try. await new Promise((r) => setTimeout(r, 1100)); // Wait for poller to poll. - await mockClient.client!.doErr({ status: 404 }); // Send error for second try. + await mockClient.client.doErr({ status: 404 }); // Send error for second try. await new Promise((r) => setTimeout(r, 200)); // Wait for poller to poll. - assert(fetchCalls >= 1); + assert(fetchObj.fetchCalls >= 1); assert(mockClient.numCreated == 2); await updater.stop(); // Pass @@ -509,36 +310,15 @@ test('FlagConfigUpdater.connect, 404, backoff to poll after 2 tries', async () = }); test('FlagConfigUpdater.connect, two starts, second does nothing', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); await new Promise((r) => setTimeout(r, 2500)); // Wait for stream to init success. - assert(fetchCalls == 0); + assert(fetchObj.fetchCalls == 0); assert(mockClient.numCreated == 1); await updater.stop(); // Pass @@ -549,34 +329,13 @@ test('FlagConfigUpdater.connect, two starts, second does nothing', async () => { }); test('FlagConfigUpdater.connect, start and immediately stop does not retry', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 100, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 100 }); try { updater.start(); updater.stop(); await new Promise((r) => setTimeout(r, 1000)); - assert(fetchCalls == 0); + assert(fetchObj.fetchCalls == 0); assert(mockClient.numCreated == 1); // Pass } catch (e) { @@ -587,59 +346,42 @@ test('FlagConfigUpdater.connect, start and immediately stop does not retry', asy test('FlagConfigUpdater.connect, test error after connection, poller starts, stream retry success, poller stops', async () => { jest.setTimeout(25000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const retryStreamFlagDelayMillis = 15000; + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ + pollingIntervalMillis: 200, + retryStreamFlagDelayMillis, + }); try { // Test error after normal close. updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); let n = mockClient.numCreated; assert(n == 1); // Pass errors to stop first stream. - await mockClient.client!.doErr({ status: 500 }); + await mockClient.client.doErr({ status: 500 }); await new Promise((r) => setTimeout(r, 200)); // Wait for stream to init. - await mockClient.client!.doErr({ status: 500 }); // Pass errors to make first retry fail. + await mockClient.client.doErr({ status: 500 }); // Pass errors to make first retry fail. n = mockClient.numCreated; assert(n == 2); await new Promise((r) => setTimeout(r, 1200)); // Wait for stream to init. - await mockClient.client!.doErr({ status: 500 }); // Pass error to make second retry fail. + await mockClient.client.doErr({ status: 500 }); // Pass error to make second retry fail. await new Promise((r) => setTimeout(r, 500)); // Wait for stream to init. // No stop() here. The streamRetryTimeout will still be running. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); n = mockClient.numCreated; assert(n == 3); // Check retry. await new Promise((r) => setTimeout(r, retryStreamFlagDelayMillis)); // Wait for retry. - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); n = mockClient.numCreated; assert(n == 4); // Check poller stop. - const prevFetchCalls = fetchCalls; + const prevFetchCalls = fetchObj.fetchCalls; await new Promise((r) => setTimeout(r, 500)); // Wait to see if poller runs while waiting. - assert(fetchCalls == prevFetchCalls); + assert(fetchObj.fetchCalls == prevFetchCalls); await updater.stop(); // Pass } catch (e) { @@ -649,69 +391,48 @@ test('FlagConfigUpdater.connect, test error after connection, poller starts, str }); test('FlagConfigUpdater.connect, test restarts', async () => { - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 200 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); let n = mockClient.numCreated; assert(n == 1); await updater.stop(); // Test start after normal close. updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); n = mockClient.numCreated; assert(n == 2); await updater.stop(); // Test error after normal close. updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - await mockClient.client!.doErr({ status: 500 }); // Send error to stop current stream. + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + await mockClient.client.doErr({ status: 500 }); // Send error to stop current stream. await new Promise((r) => setTimeout(r, 200)); // Wait for stream to init. - await mockClient.client!.doErr({ status: 500 }); // Send error for first retry. + await mockClient.client.doErr({ status: 500 }); // Send error for first retry. await new Promise((r) => setTimeout(r, 1200)); // Wait for stream to timeout and start second try. - await mockClient.client!.doErr({ status: 500 }); // Send error for second retry. + await mockClient.client.doErr({ status: 500 }); // Send error for second retry. await new Promise((r) => setTimeout(r, 500)); // Wait for stream to init. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); n = mockClient.numCreated; assert(n == 5); // No stop() here. The streamRetryTimeout will still be running. // Test normal start after error close. Poller should be stopped. - const prevFetchCalls = fetchCalls; + const prevFetchCalls = fetchObj.fetchCalls; updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); await new Promise((r) => setTimeout(r, 500)); // Wait for stream to init. - assert(fetchCalls == prevFetchCalls); + assert(fetchObj.fetchCalls == prevFetchCalls); n = mockClient.numCreated; assert(n == 6); await updater.stop(); @@ -724,46 +445,25 @@ test('FlagConfigUpdater.connect, test restarts', async () => { test('FlagConfigUpdater.connect, start success, keep alive success, no fallback to poller', async () => { jest.setTimeout(20000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 200 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); let n = mockClient.numCreated; assert(n == 1); // Test keep alive. await new Promise((r) => setTimeout(r, 15000)); // Wait before keep alive timeouts. - await mockClient.client!.doMsg({ data: ' ' }); - assert(fetchCalls == 0); + await mockClient.client.doMsg({ data: ' ' }); + assert(fetchObj.fetchCalls == 0); n = mockClient.numCreated; assert(n == 1); await new Promise((r) => setTimeout(r, 3000)); // Wait for original keep alive timeout to reach. - assert(fetchCalls == 0); + assert(fetchObj.fetchCalls == 0); n = mockClient.numCreated; assert(n == 1); @@ -777,42 +477,21 @@ test('FlagConfigUpdater.connect, start success, keep alive success, no fallback test('FlagConfigStreamer.connect, start success, keep alive fail, retry success', async () => { jest.setTimeout(20000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 200 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); let n = mockClient.numCreated; assert(n == 1); // Test keep alive fail. await new Promise((r) => setTimeout(r, 17500)); // Wait for keep alive to fail and enter retry. - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); n = mockClient.numCreated; assert(n == 2); @@ -826,44 +505,23 @@ test('FlagConfigStreamer.connect, start success, keep alive fail, retry success' test('FlagConfigUpdater.connect, start success, keep alive fail, retry fail twice, fallback to poller', async () => { jest.setTimeout(20000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - retryStreamFlagDelayMillis, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ pollingIntervalMillis: 200 }); try { updater.start(); - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == 0); + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == 0); let n = mockClient.numCreated; assert(n == 1); // Test keep alive fail. await new Promise((r) => setTimeout(r, 17500)); // Wait for keep alive to fail and enter retry. - await mockClient.client!.doErr({ status: 500 }); // Send error for first try. + await mockClient.client.doErr({ status: 500 }); // Send error for first try. await new Promise((r) => setTimeout(r, 1200)); // Wait for stream to init. - await mockClient.client!.doErr({ status: 500 }); // Send error for second try. + await mockClient.client.doErr({ status: 500 }); // Send error for second try. await new Promise((r) => setTimeout(r, 500)); // Wait for poller to init. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); n = mockClient.numCreated; assert(n == 3); @@ -877,34 +535,16 @@ test('FlagConfigUpdater.connect, start success, keep alive fail, retry fail twic test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream success, stop poller, no more retry stream', async () => { jest.setTimeout(10000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - 2000, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ + pollingIntervalMillis: 200, + retryStreamFlagDelayMillis: 2000, + }); try { updater.start(); - await mockClient.client!.doErr({ status: 501 }); // Fatal err to fail initial conn. + await mockClient.client.doErr({ status: 501 }); // Fatal err to fail initial conn. await new Promise((r) => setTimeout(r, 500)); // Wait for poller to start. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); let n = mockClient.numCreated; assert(n == 1); @@ -914,14 +554,14 @@ test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream su assert(n == 2); // Retry stream success. - const prevFetchCalls = fetchCalls; - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == prevFetchCalls); + const prevFetchCalls = fetchObj.fetchCalls; + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == prevFetchCalls); // Wait to check poller stopped. await new Promise((r) => setTimeout(r, 500)); - assert(fetchCalls == prevFetchCalls); + assert(fetchObj.fetchCalls == prevFetchCalls); // Check there is no more retry stream. await new Promise((r) => setTimeout(r, 2000)); // Wait for retry. @@ -938,34 +578,16 @@ test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream su test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream fail, continue poller, retry stream success, stop poller', async () => { jest.setTimeout(10000); - const mockClient = getNewClient(); - let fetchCalls = 0; - const mockFetcher = new FlagConfigFetcher( - apiKey, - new MockHttpClient(async () => { - fetchCalls++; - return { status: 200, body: '[]' }; - }), - ); - const updater = new FlagConfigStreamer( - apiKey, - mockFetcher, - cache, - mockClient.clientClass, - 200, // poller fetch every 100ms. - streamConnTimeoutMillis, - streamFlagConnTimeoutMillis, - streamFlagTryAttempts, - streamFlagTryDelayMillis, - 2000, - serverUrl, - false, - ); + const { fetchObj, fetcherReturnNext, cache, mockClient, updater } = + getTestObjs({ + pollingIntervalMillis: 200, + retryStreamFlagDelayMillis: 2000, + }); try { updater.start(); - await mockClient.client!.doErr({ status: 501 }); // Fatal err to fail initial conn. + await mockClient.client.doErr({ status: 501 }); // Fatal err to fail initial conn. await new Promise((r) => setTimeout(r, 500)); // Wait for poller to start. - assert(fetchCalls > 0); + assert(fetchObj.fetchCalls > 0); let n = mockClient.numCreated; assert(n == 1); @@ -975,12 +597,12 @@ test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream fa assert(n == 2); // Retry stream fail. - let prevFetchCalls = fetchCalls; - await mockClient.client!.doErr({ status: 500 }); // Fatal err to fail stream retry. + let prevFetchCalls = fetchObj.fetchCalls; + await mockClient.client.doErr({ status: 500 }); // Fatal err to fail stream retry. // Wait to check poller continues to poll. await new Promise((r) => setTimeout(r, 500)); - assert(fetchCalls > prevFetchCalls); + assert(fetchObj.fetchCalls > prevFetchCalls); // Wait for another retry stream start. await new Promise((r) => setTimeout(r, 2000)); // Wait for retry. @@ -988,14 +610,14 @@ test('FlagConfigUpdater.connect, start fail, fallback to poller, retry stream fa assert(n == 3); // Retry stream success. - prevFetchCalls = fetchCalls; - await mockClient.client!.doOpen({ type: 'open' }); - await mockClient.client!.doMsg({ data: '[]' }); - assert(fetchCalls == prevFetchCalls); + prevFetchCalls = fetchObj.fetchCalls; + await mockClient.client.doOpen({ type: 'open' }); + await mockClient.client.doMsg({ data: '[]' }); + assert(fetchObj.fetchCalls == prevFetchCalls); // Wait to check poller stopped. await new Promise((r) => setTimeout(r, 500)); - assert(fetchCalls == prevFetchCalls); + assert(fetchObj.fetchCalls == prevFetchCalls); await updater.stop(); // Pass