@prosopo/load-balancer 2.11.0 → 2.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { _resetHealthzRetryPolicy, _resetPinCache, _resetProviderListCache, _setHealthzRetryPolicy, getProviders, getRandomActiveProvider, getRandomProviderFromList, } from "../providers.js";
2
+ import { PROVIDER_LIST_MISS_RELOAD_MS, PROVIDER_LIST_TTL_MS, _resetHealthzRetryPolicy, _resetPinCache, _resetProviderListCache, _setHealthzRetryPolicy, findProvider, getProviders, getRandomActiveProvider, getRandomProviderFromList, } from "../providers.js";
3
3
  const loadBalancer = vi.fn();
4
4
  const getProviderListOverride = vi.fn(() => []);
5
5
  vi.mock("../balancer.js", () => ({
@@ -16,12 +16,21 @@ const mockHealthzFetch = (host, ok = true, status = 200) => {
16
16
  globalThis.fetch = mocked;
17
17
  return mocked;
18
18
  };
19
+ const originalDateNow = Date.now;
20
+ const CLOCK_START = 1_700_000_000_000;
21
+ let clock = CLOCK_START;
22
+ const advanceClock = (ms) => {
23
+ clock += ms;
24
+ };
19
25
  beforeEach(() => {
20
26
  _resetPinCache();
27
+ clock = CLOCK_START;
28
+ Date.now = () => clock;
21
29
  _setHealthzRetryPolicy({ baseDelayMs: 0, maxDelayMs: 0 });
22
30
  });
23
31
  afterEach(() => {
24
32
  globalThis.fetch = originalFetch;
33
+ Date.now = originalDateNow;
25
34
  _resetHealthzRetryPolicy();
26
35
  });
27
36
  describe("getRandomActiveProvider (dual stack)", () => {
@@ -168,6 +177,104 @@ describe("getProviders", () => {
168
177
  expect(retry).toBe(providers);
169
178
  expect(loadBalancer).toHaveBeenCalledTimes(2);
170
179
  });
180
+ it("keeps a separate entry per ipMode and passes it to loadBalancer", async () => {
181
+ const dual = [
182
+ { address: "5xyz", url: "https://p1", datasetId: "0x0", weight: 1 },
183
+ ];
184
+ const v4 = [
185
+ { address: "5xyz", url: "https://ipv4.p1", datasetId: "0x0", weight: 1 },
186
+ ];
187
+ loadBalancer.mockImplementation(async (_env, ipMode) => ipMode === "ipv4" ? v4 : dual);
188
+ expect(await getProviders("production")).toBe(dual);
189
+ expect(await getProviders("production", "ipv4")).toBe(v4);
190
+ expect(await getProviders("production")).toBe(dual);
191
+ expect(await getProviders("production", "ipv4")).toBe(v4);
192
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
193
+ expect(loadBalancer).toHaveBeenCalledWith("production", undefined);
194
+ expect(loadBalancer).toHaveBeenCalledWith("production", "ipv4");
195
+ });
196
+ it("reloads once the cached list is older than the TTL", async () => {
197
+ const first = [
198
+ { address: "5A", url: "https://p1", datasetId: "0x0", weight: 1 },
199
+ ];
200
+ const second = [
201
+ { address: "5B", url: "https://p2", datasetId: "0x0", weight: 1 },
202
+ ];
203
+ loadBalancer.mockResolvedValueOnce(first).mockResolvedValueOnce(second);
204
+ expect(await getProviders("production")).toBe(first);
205
+ advanceClock(PROVIDER_LIST_TTL_MS - 1);
206
+ expect(await getProviders("production")).toBe(first);
207
+ advanceClock(1);
208
+ expect(await getProviders("production")).toBe(second);
209
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
210
+ });
211
+ });
212
+ describe("findProvider", () => {
213
+ const providers = [
214
+ { address: "5A", url: "https://pronode1.io", datasetId: "0x0", weight: 1 },
215
+ { address: "5B", url: "https://pronode2.io", datasetId: "0x0", weight: 1 },
216
+ ];
217
+ beforeEach(() => {
218
+ _resetProviderListCache();
219
+ loadBalancer.mockReset();
220
+ });
221
+ it("matches the issuing provider by exact url", async () => {
222
+ loadBalancer.mockResolvedValue(providers);
223
+ const found = await findProvider("production", "https://pronode2.io");
224
+ expect(found?.address).toBe("5B");
225
+ expect(loadBalancer).toHaveBeenCalledTimes(1);
226
+ });
227
+ it("serves a hit from cache without reloading the list", async () => {
228
+ loadBalancer.mockResolvedValue(providers);
229
+ await findProvider("production", "https://pronode1.io");
230
+ await findProvider("production", "https://pronode1.io");
231
+ await findProvider("production", "https://pronode1.io");
232
+ expect(loadBalancer).toHaveBeenCalledTimes(1);
233
+ });
234
+ it("looks in the ipMode section the token was minted against", async () => {
235
+ const v4 = [
236
+ {
237
+ address: "5A",
238
+ url: "https://ipv4.pronode1.io",
239
+ datasetId: "0x0",
240
+ weight: 1,
241
+ },
242
+ ];
243
+ loadBalancer.mockImplementation(async (_env, ipMode) => ipMode === "ipv4" ? v4 : providers);
244
+ const found = await findProvider("production", "https://ipv4.pronode1.io", "ipv4");
245
+ expect(found?.address).toBe("5A");
246
+ });
247
+ it("reloads on a miss so a provider added mid-TTL still verifies", async () => {
248
+ const added = [
249
+ ...providers,
250
+ {
251
+ address: "5C",
252
+ url: "https://pronode3.io",
253
+ datasetId: "0x0",
254
+ weight: 1,
255
+ },
256
+ ];
257
+ loadBalancer.mockResolvedValueOnce(providers).mockResolvedValueOnce(added);
258
+ await findProvider("production", "https://pronode1.io");
259
+ advanceClock(PROVIDER_LIST_MISS_RELOAD_MS);
260
+ const found = await findProvider("production", "https://pronode3.io");
261
+ expect(found?.address).toBe("5C");
262
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
263
+ });
264
+ it("rate-limits the reload so an unknown url can't refetch per request", async () => {
265
+ loadBalancer.mockResolvedValue(providers);
266
+ await findProvider("production", "https://pronode1.io");
267
+ advanceClock(PROVIDER_LIST_MISS_RELOAD_MS);
268
+ expect(await findProvider("production", "https://attacker.io")).toBe(undefined);
269
+ expect(await findProvider("production", "https://attacker.io")).toBe(undefined);
270
+ expect(await findProvider("production", "https://attacker.io")).toBe(undefined);
271
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
272
+ });
273
+ it("returns undefined for a token with no providerUrl without loading", async () => {
274
+ loadBalancer.mockResolvedValue(providers);
275
+ expect(await findProvider("production", undefined)).toBe(undefined);
276
+ expect(loadBalancer).not.toHaveBeenCalled();
277
+ });
171
278
  });
172
279
  describe("getRandomProviderFromList", () => {
173
280
  const providerList = [
@@ -1 +1 @@
1
- {"version":3,"file":"providers.unit.test.js","sourceRoot":"","sources":["../../src/tests/providers.unit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"providers.unit.test.js","sourceRoot":"","sources":["../../src/tests/providers.unit.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/load-balancer",
3
- "version": "2.11.0",
3
+ "version": "2.11.2",
4
4
  "description": "Provider load balancer",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,11 +37,11 @@
37
37
  },
38
38
  "homepage": "https://github.com/prosopo/captcha#readme",
39
39
  "dependencies": {
40
- "@prosopo/common": "3.1.59",
41
- "@prosopo/types": "5.10.1"
40
+ "@prosopo/common": "3.1.60",
41
+ "@prosopo/types": "5.11.0"
42
42
  },
43
43
  "devDependencies": {
44
- "@prosopo/config": "3.4.1",
44
+ "@prosopo/config": "3.4.2",
45
45
  "@types/node": "22.10.2",
46
46
  "@vitest/coverage-v8": "4.1.11",
47
47
  "concurrently": "9.0.1",
package/src/balancer.ts CHANGED
@@ -166,9 +166,26 @@ export const getLoadBalancerUrl = (environment: EnvironmentTypes): string => {
166
166
  });
167
167
  };
168
168
 
169
+ /**
170
+ * Upper bound on the provider-list fetch. `@prosopo/server` calls
171
+ * `loadBalancer` on every `isVerified`, so a stalled list endpoint would
172
+ * otherwise hold the site owner's request open for the runtime's default
173
+ * (~300s in undici).
174
+ */
175
+ export const PROVIDER_LIST_FETCH_TIMEOUT_MS = 10_000;
176
+
177
+ const timeoutSignal = (timeoutMs: number): AbortSignal | undefined =>
178
+ // Older browsers running the widget lack AbortSignal.timeout; there the
179
+ // fetch keeps its previous unbounded behaviour rather than throwing.
180
+ typeof AbortSignal !== "undefined" &&
181
+ typeof AbortSignal.timeout === "function"
182
+ ? AbortSignal.timeout(timeoutMs)
183
+ : undefined;
184
+
169
185
  export const loadBalancer = async (
170
186
  environment: EnvironmentTypes,
171
187
  ipMode?: IpMode,
188
+ timeoutMs: number = PROVIDER_LIST_FETCH_TIMEOUT_MS,
172
189
  ): Promise<HardcodedProvider[]> => {
173
190
  // An override supplies the whole list. Checked before the environment
174
191
  // branches so a self-hosted deployment answers for `production` too, and
@@ -197,6 +214,7 @@ export const loadBalancer = async (
197
214
  {
198
215
  method: "GET",
199
216
  mode: "cors",
217
+ signal: timeoutSignal(timeoutMs),
200
218
  },
201
219
  ).then((res) => res.json());
202
220
  // `ipMode` steers `convertHostedProvider` at the fetched JSON's
package/src/providers.ts CHANGED
@@ -155,13 +155,27 @@ const resolvePinnedUrl = async (
155
155
  return promise;
156
156
  };
157
157
 
158
- // Cached, in-flight provider-list load per env. The list rarely changes, so a
159
- // single fetch is shared across callers rather than re-fetching the
160
- // provider-list JSON on every verify-forward decision.
161
- const providerListPromiseCache: Map<
162
- EnvironmentTypes,
163
- Promise<HardcodedProvider[]>
164
- > = new Map();
158
+ // How long a loaded list is served before the next caller reloads it. The
159
+ // hosted list advertises `cache-control: max-age=300`, so this matches what it
160
+ // asks for. `findProvider` covers the window where a pronode is added mid-TTL.
161
+ export const PROVIDER_LIST_TTL_MS = 5 * 60 * 1000;
162
+
163
+ // Floor on how often a lookup miss may force an early reload. `providerUrl` is
164
+ // read from the token, which a caller controls, so an unknown url must not be
165
+ // able to turn every request into a fresh fetch of the list.
166
+ export const PROVIDER_LIST_MISS_RELOAD_MS = 10 * 1000;
167
+
168
+ interface ProviderListEntry {
169
+ providers: Promise<HardcodedProvider[]>;
170
+ loadedAt: number;
171
+ }
172
+
173
+ // Cached, in-flight provider-list load per env + ipMode. The list rarely
174
+ // changes, so a single fetch is shared across callers rather than re-fetching
175
+ // the provider-list JSON on every verify decision. Keyed the same way as the
176
+ // pin cache: the `ipv4`/`ipv6` sections carry different urls to the dual-stack
177
+ // default, so they cannot share an entry.
178
+ const providerListCache: Map<CacheKey, ProviderListEntry> = new Map();
165
179
 
166
180
  /**
167
181
  * Returns the full (cached) list of active providers for an environment.
@@ -170,18 +184,59 @@ const providerListPromiseCache: Map<
170
184
  */
171
185
  export const getProviders = async (
172
186
  env: EnvironmentTypes,
187
+ ipMode?: IpMode,
173
188
  ): Promise<HardcodedProvider[]> => {
174
- const cached = providerListPromiseCache.get(env);
175
- if (cached) return cached;
189
+ const key = cacheKey(env, ipMode);
190
+ const cached = providerListCache.get(key);
191
+ if (cached && Date.now() - cached.loadedAt < PROVIDER_LIST_TTL_MS) {
192
+ return cached.providers;
193
+ }
176
194
 
177
- const promise = loadBalancer(env).catch((err) => {
195
+ const providers = loadBalancer(env, ipMode).catch((err) => {
178
196
  // Don't cache failures — a transient fetch error shouldn't poison the
179
- // cache for the lifetime of the process.
180
- providerListPromiseCache.delete(env);
197
+ // cache for the lifetime of the process. Only evict this entry: a
198
+ // later load may already have replaced it.
199
+ if (providerListCache.get(key)?.providers === providers) {
200
+ providerListCache.delete(key);
201
+ }
181
202
  throw err;
182
203
  });
183
- providerListPromiseCache.set(env, promise);
184
- return promise;
204
+ providerListCache.set(key, { providers, loadedAt: Date.now() });
205
+ return providers;
206
+ };
207
+
208
+ /**
209
+ * Finds the provider that minted a token, by exact url match.
210
+ *
211
+ * Exact-string rather than normalised: the single-stack sections of the list
212
+ * carry sub-zone urls (`https://ipv4.pronode15.prosopo.io`), so the caller
213
+ * passes the `ipMode` it derived from the token's own hostname and both sides
214
+ * of the comparison stay in the same section.
215
+ *
216
+ * A miss reloads the list once before giving up, so a pronode added part-way
217
+ * through the TTL still verifies the tokens it has already minted. That reload
218
+ * is rate-limited, because a miss is also what an unknown url produces.
219
+ */
220
+ export const findProvider = async (
221
+ env: EnvironmentTypes,
222
+ providerUrl: string | undefined,
223
+ ipMode?: IpMode,
224
+ ): Promise<HardcodedProvider | undefined> => {
225
+ if (!providerUrl) return undefined;
226
+
227
+ const providers = await getProviders(env, ipMode);
228
+ const provider = providers.find((p) => p.url === providerUrl);
229
+ if (provider) return provider;
230
+
231
+ const key = cacheKey(env, ipMode);
232
+ const cached = providerListCache.get(key);
233
+ if (cached && Date.now() - cached.loadedAt < PROVIDER_LIST_MISS_RELOAD_MS) {
234
+ return undefined;
235
+ }
236
+ providerListCache.delete(key);
237
+
238
+ const reloaded = await getProviders(env, ipMode);
239
+ return reloaded.find((p) => p.url === providerUrl);
185
240
  };
186
241
 
187
242
  export const getRandomActiveProvider = async (
@@ -270,7 +325,7 @@ export const _resetPinCache = () => {
270
325
  // Test-only escape hatch to isolate the provider-list cache between cases.
271
326
  // Not exported from the package index — internal use only.
272
327
  export const _resetProviderListCache = () => {
273
- providerListPromiseCache.clear();
328
+ providerListCache.clear();
274
329
  };
275
330
 
276
331
  // Test-only override for the healthz retry policy — tests use it to disable
@@ -22,6 +22,7 @@
22
22
 
23
23
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
24
  import {
25
+ PROVIDER_LIST_FETCH_TIMEOUT_MS,
25
26
  convertHostedProvider,
26
27
  getProviderHostname,
27
28
  loadBalancer,
@@ -228,3 +229,39 @@ describe("getRandomProviderFromList (production.json end-to-end)", () => {
228
229
  expect(mocked).toHaveBeenCalledTimes(1);
229
230
  });
230
231
  });
232
+
233
+ describe("loadBalancer provider-list fetch timeout", () => {
234
+ // A list endpoint that accepts the request and never answers: the fetch
235
+ // only settles if the caller supplied an abort signal.
236
+ const stallingFetch = () =>
237
+ vi.fn(
238
+ (_input: RequestInfo | URL, init?: RequestInit): Promise<Response> =>
239
+ new Promise<Response>((_resolve, reject) => {
240
+ init?.signal?.addEventListener("abort", () =>
241
+ reject(init.signal?.reason),
242
+ );
243
+ }),
244
+ );
245
+
246
+ it("aborts a stalled provider-list fetch after the timeout", async () => {
247
+ const mocked = stallingFetch();
248
+ globalThis.fetch = mocked as typeof fetch;
249
+ const started = Date.now();
250
+ await expect(
251
+ loadBalancer("production", undefined, 50),
252
+ ).rejects.toMatchObject({ name: "TimeoutError" });
253
+ expect(Date.now() - started).toBeLessThan(2_000);
254
+ });
255
+
256
+ it("bounds the fetch by default", async () => {
257
+ const mocked = vi.fn(
258
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
259
+ new Response(JSON.stringify(productionJson), { status: 200 }),
260
+ );
261
+ globalThis.fetch = mocked as typeof fetch;
262
+ await loadBalancer("production");
263
+ const init = mocked.mock.calls[0]?.[1];
264
+ expect(init?.signal).toBeInstanceOf(AbortSignal);
265
+ expect(PROVIDER_LIST_FETCH_TIMEOUT_MS).toBe(10_000);
266
+ });
267
+ });
@@ -12,13 +12,17 @@
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
 
15
+ import type { EnvironmentTypes } from "@prosopo/types";
15
16
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
- import type { HardcodedProvider } from "../balancer.js";
17
+ import type { HardcodedProvider, IpMode } from "../balancer.js";
17
18
  import {
19
+ PROVIDER_LIST_MISS_RELOAD_MS,
20
+ PROVIDER_LIST_TTL_MS,
18
21
  _resetHealthzRetryPolicy,
19
22
  _resetPinCache,
20
23
  _resetProviderListCache,
21
24
  _setHealthzRetryPolicy,
25
+ findProvider,
22
26
  getProviders,
23
27
  getRandomActiveProvider,
24
28
  getRandomProviderFromList,
@@ -51,8 +55,19 @@ const mockHealthzFetch = (host: string, ok = true, status = 200) => {
51
55
  return mocked;
52
56
  };
53
57
 
58
+ // The provider-list cache ages off Date.now(), so drive it from the test
59
+ // rather than sleeping. Nothing else in this package reads the clock.
60
+ const originalDateNow = Date.now;
61
+ const CLOCK_START = 1_700_000_000_000;
62
+ let clock = CLOCK_START;
63
+ const advanceClock = (ms: number) => {
64
+ clock += ms;
65
+ };
66
+
54
67
  beforeEach(() => {
55
68
  _resetPinCache();
69
+ clock = CLOCK_START;
70
+ Date.now = () => clock;
56
71
  // Keep the retry policy but drop backoff to zero so failure-path tests
57
72
  // don't sit waiting on real timers.
58
73
  _setHealthzRetryPolicy({ baseDelayMs: 0, maxDelayMs: 0 });
@@ -60,6 +75,7 @@ beforeEach(() => {
60
75
 
61
76
  afterEach(() => {
62
77
  globalThis.fetch = originalFetch;
78
+ Date.now = originalDateNow;
63
79
  _resetHealthzRetryPolicy();
64
80
  });
65
81
 
@@ -249,6 +265,149 @@ describe("getProviders", () => {
249
265
  expect(retry).toBe(providers);
250
266
  expect(loadBalancer).toHaveBeenCalledTimes(2);
251
267
  });
268
+
269
+ it("keeps a separate entry per ipMode and passes it to loadBalancer", async () => {
270
+ const dual: HardcodedProvider[] = [
271
+ { address: "5xyz", url: "https://p1", datasetId: "0x0", weight: 1 },
272
+ ];
273
+ const v4: HardcodedProvider[] = [
274
+ { address: "5xyz", url: "https://ipv4.p1", datasetId: "0x0", weight: 1 },
275
+ ];
276
+ loadBalancer.mockImplementation(
277
+ async (_env: EnvironmentTypes, ipMode?: IpMode) =>
278
+ ipMode === "ipv4" ? v4 : dual,
279
+ );
280
+
281
+ expect(await getProviders("production")).toBe(dual);
282
+ expect(await getProviders("production", "ipv4")).toBe(v4);
283
+ // Both are now cached independently.
284
+ expect(await getProviders("production")).toBe(dual);
285
+ expect(await getProviders("production", "ipv4")).toBe(v4);
286
+
287
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
288
+ expect(loadBalancer).toHaveBeenCalledWith("production", undefined);
289
+ expect(loadBalancer).toHaveBeenCalledWith("production", "ipv4");
290
+ });
291
+
292
+ it("reloads once the cached list is older than the TTL", async () => {
293
+ const first: HardcodedProvider[] = [
294
+ { address: "5A", url: "https://p1", datasetId: "0x0", weight: 1 },
295
+ ];
296
+ const second: HardcodedProvider[] = [
297
+ { address: "5B", url: "https://p2", datasetId: "0x0", weight: 1 },
298
+ ];
299
+ loadBalancer.mockResolvedValueOnce(first).mockResolvedValueOnce(second);
300
+
301
+ expect(await getProviders("production")).toBe(first);
302
+ advanceClock(PROVIDER_LIST_TTL_MS - 1);
303
+ expect(await getProviders("production")).toBe(first);
304
+ advanceClock(1);
305
+ expect(await getProviders("production")).toBe(second);
306
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
307
+ });
308
+ });
309
+
310
+ describe("findProvider", () => {
311
+ const providers: HardcodedProvider[] = [
312
+ { address: "5A", url: "https://pronode1.io", datasetId: "0x0", weight: 1 },
313
+ { address: "5B", url: "https://pronode2.io", datasetId: "0x0", weight: 1 },
314
+ ];
315
+
316
+ beforeEach(() => {
317
+ _resetProviderListCache();
318
+ loadBalancer.mockReset();
319
+ });
320
+
321
+ it("matches the issuing provider by exact url", async () => {
322
+ loadBalancer.mockResolvedValue(providers);
323
+
324
+ const found = await findProvider("production", "https://pronode2.io");
325
+
326
+ expect(found?.address).toBe("5B");
327
+ expect(loadBalancer).toHaveBeenCalledTimes(1);
328
+ });
329
+
330
+ it("serves a hit from cache without reloading the list", async () => {
331
+ loadBalancer.mockResolvedValue(providers);
332
+
333
+ await findProvider("production", "https://pronode1.io");
334
+ await findProvider("production", "https://pronode1.io");
335
+ await findProvider("production", "https://pronode1.io");
336
+
337
+ expect(loadBalancer).toHaveBeenCalledTimes(1);
338
+ });
339
+
340
+ it("looks in the ipMode section the token was minted against", async () => {
341
+ const v4: HardcodedProvider[] = [
342
+ {
343
+ address: "5A",
344
+ url: "https://ipv4.pronode1.io",
345
+ datasetId: "0x0",
346
+ weight: 1,
347
+ },
348
+ ];
349
+ loadBalancer.mockImplementation(
350
+ async (_env: EnvironmentTypes, ipMode?: IpMode) =>
351
+ ipMode === "ipv4" ? v4 : providers,
352
+ );
353
+
354
+ const found = await findProvider(
355
+ "production",
356
+ "https://ipv4.pronode1.io",
357
+ "ipv4",
358
+ );
359
+
360
+ expect(found?.address).toBe("5A");
361
+ });
362
+
363
+ it("reloads on a miss so a provider added mid-TTL still verifies", async () => {
364
+ const added: HardcodedProvider[] = [
365
+ ...providers,
366
+ {
367
+ address: "5C",
368
+ url: "https://pronode3.io",
369
+ datasetId: "0x0",
370
+ weight: 1,
371
+ },
372
+ ];
373
+ loadBalancer.mockResolvedValueOnce(providers).mockResolvedValueOnce(added);
374
+
375
+ // Warm the cache with the list that predates pronode3.
376
+ await findProvider("production", "https://pronode1.io");
377
+ advanceClock(PROVIDER_LIST_MISS_RELOAD_MS);
378
+
379
+ const found = await findProvider("production", "https://pronode3.io");
380
+
381
+ expect(found?.address).toBe("5C");
382
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
383
+ });
384
+
385
+ it("rate-limits the reload so an unknown url can't refetch per request", async () => {
386
+ loadBalancer.mockResolvedValue(providers);
387
+
388
+ await findProvider("production", "https://pronode1.io");
389
+ advanceClock(PROVIDER_LIST_MISS_RELOAD_MS);
390
+ // First miss after the floor is allowed to reload...
391
+ expect(await findProvider("production", "https://attacker.io")).toBe(
392
+ undefined,
393
+ );
394
+ // ...the ones immediately behind it are not.
395
+ expect(await findProvider("production", "https://attacker.io")).toBe(
396
+ undefined,
397
+ );
398
+ expect(await findProvider("production", "https://attacker.io")).toBe(
399
+ undefined,
400
+ );
401
+
402
+ expect(loadBalancer).toHaveBeenCalledTimes(2);
403
+ });
404
+
405
+ it("returns undefined for a token with no providerUrl without loading", async () => {
406
+ loadBalancer.mockResolvedValue(providers);
407
+
408
+ expect(await findProvider("production", undefined)).toBe(undefined);
409
+ expect(loadBalancer).not.toHaveBeenCalled();
410
+ });
252
411
  });
253
412
 
254
413
  describe("getRandomProviderFromList", () => {