diff --git a/src/__tests__/endpoint.test.ts b/src/__tests__/endpoint.test.ts index d9aec25a..52fa0943 100644 --- a/src/__tests__/endpoint.test.ts +++ b/src/__tests__/endpoint.test.ts @@ -1,5 +1,5 @@ import { describe, expect, mock, test } from 'bun:test'; -import { executeEndpointFallback } from '../endpoint'; +import { executeEndpointFallback, pickRandomEndpoint } from '../endpoint'; const delay = (ms: number) => new Promise(resolve => { @@ -90,3 +90,34 @@ describe('executeEndpointFallback', () => { expect(tryEndpoint.mock.calls.map(call => call[0])).toEqual(['a', 'b', 'c']); }); }); + +describe('pickRandomEndpoint', () => { + test('returns undefined for empty arrays', () => { + expect(pickRandomEndpoint([])).toBeUndefined(); + }); + + test('deterministically selects an endpoint using a custom random parameter', () => { + const endpoints = ['a', 'b', 'c']; + + // Test random = 0 (picks first) + const endpoints1 = [...endpoints]; + expect(pickRandomEndpoint(endpoints1, () => 0)).toBe('a'); + + // Test random = 0.5 (picks middle) + const endpoints2 = [...endpoints]; + expect(pickRandomEndpoint(endpoints2, () => 0.5)).toBe('b'); + + // Test random = 0.99 (picks last) + const endpoints3 = [...endpoints]; + expect(pickRandomEndpoint(endpoints3, () => 0.99)).toBe('c'); + }); + + test('mutates the original array by removing the selected endpoint', () => { + const endpoints = ['a', 'b', 'c']; + const result = pickRandomEndpoint(endpoints, () => 0.5); + + expect(result).toBe('b'); + expect(endpoints).toEqual(['a', 'c']); + expect(endpoints.length).toBe(2); + }); +}); diff --git a/src/endpoint.ts b/src/endpoint.ts index 0c4fb86f..67cbbac6 100644 --- a/src/endpoint.ts +++ b/src/endpoint.ts @@ -44,12 +44,11 @@ export const dedupeEndpoints = ( export const pickRandomEndpoint = ( endpoints: string[], - random: () => number = Math.random, -) => { - if (!endpoints.length) { - throw new Error('No endpoints configured'); - } - return endpoints[Math.floor(random() * endpoints.length)]; + random = Math.random, +): string | undefined => { + if (endpoints.length === 0) return undefined; + const index = Math.floor(random() * endpoints.length); + return endpoints.splice(index, 1)[0]; }; export async function selectFastestSuccessfulEndpoint( @@ -124,6 +123,9 @@ export async function executeEndpointFallback({ } const firstEndpoint = pickRandomEndpoint(candidates, random); + if (!firstEndpoint) { + throw new Error('No endpoints configured'); + } try { return {