Skip to content

Commit

Permalink
Updating unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalrymple committed Aug 8, 2023
1 parent 51d39ff commit 1f0ce18
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 15 deletions.
2 changes: 1 addition & 1 deletion packages/requester-utils/src/BaseResource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RequesterType, ResourceOptions, RateLimitOptions } from './RequesterUtils';
import { RateLimitOptions, RequesterType, ResourceOptions } from './RequesterUtils';

export interface RootResourceOptions<C> {
// TODO: Not actually optional - Need to fix wrapper typing in requestUtils.ts:
Expand Down
15 changes: 8 additions & 7 deletions packages/requester-utils/src/RequesterUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export type ResourceOptions = {
headers: { [header: string]: string };
authHeaders: { [authHeader: string]: () => Promise<string> };
url: string;
rateLimits: RateLimitOptions;
rejectUnauthorized: boolean;
rateLimits?: RateLimitOptions;
};

export type DefaultRequestOptions = {
Expand Down Expand Up @@ -222,25 +222,26 @@ export function presetResourceArguments<T extends Record<string, Constructable>>
return updated as T;
}

export function getMatchingRateLimit(
export function getMatchingRateLimiter(
endpoint: string,
method: string = 'GET',
rateLimiters: RateLimiters = {},
method: string = 'GET',
): ReturnType<typeof RateLimit> {
const sortedEndpoints = Object.keys(rateLimiters).sort();
const sortedEndpoints = Object.keys(rateLimiters).sort().reverse();

// eslint-disable-next-line
for (const ep of sortedEndpoints) {
if (micromatch([endpoint], ep)) {
const rateLimitConfig = rateLimiters[ep];

if ('method' in rateLimitConfig) {
if (rateLimitConfig.method === method.toUpperCase()) return rateLimitConfig.limit;
if (typeof rateLimitConfig === 'object' && 'method' in rateLimitConfig) {
if (rateLimitConfig.method.toUpperCase() === method.toUpperCase())
return rateLimitConfig.limit;
} else {
return rateLimitConfig;
}
}
}

return RateLimit(30);
return RateLimit(1000);
}
8 changes: 8 additions & 0 deletions packages/requester-utils/test/unit/BaseResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ describe('Creation of BaseResource instance', () => {
await expect(service.authHeaders['job-token']()).resolves.toBe('1234');
});

it('should throw an error if a token, jobToken or oauthToken is not passed', () => {
expect(() => {
// eslint-disable-next-line
// @ts-ignore
new BaseResource({ requesterFn: jest.fn() }); // eslint-disable-line
}).toThrow();
});

it('should set the X-Profile-Token header if the profileToken option is given', () => {
const service = new BaseResource({
token: '123',
Expand Down
66 changes: 64 additions & 2 deletions packages/requester-utils/test/unit/RequesterUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createRequesterFn,
defaultOptionsHandler,
formatQuery,
getMatchingRateLimiter,
presetResourceArguments,
} from '../../src/RequesterUtils';

Expand Down Expand Up @@ -116,7 +117,6 @@ describe('createInstance', () => {
},
url: 'testurl',
rejectUnauthorized: false,
rateLimits: {},
};

it('should have a createInstance function', () => {
Expand Down Expand Up @@ -146,7 +146,7 @@ describe('createInstance', () => {
serviceOptions,
expect.objectContaining({ method: m.toUpperCase() }),
);
expect(requestHandler).toHaveBeenCalledWith(testEndpoint, {});
expect(requestHandler).toHaveBeenCalledWith(testEndpoint, { rateLimiters: {} });
}
});

Expand Down Expand Up @@ -310,3 +310,65 @@ describe('formatQuery', () => {
expect(string).toBe('test=6&not%5Btest%5D=7');
});
});

describe('getMatchingRateLimiter', () => {
it('should default the method to GET if not passed', () => {
const rateLimiter = 10;
const matchingRateLimiter = getMatchingRateLimiter('endpoint', {
'*': { method: 'GET', limit: rateLimiter },
});

expect(matchingRateLimiter).toBe(rateLimiter);
});

it('should uppercase method for matching', () => {
const rateLimiter = 10;
const matchingRateLimiter = getMatchingRateLimiter('endpoint', {
'*': { method: 'get', limit: rateLimiter },
});

expect(matchingRateLimiter).toBe(rateLimiter);
});

it('should default the rateLimiters to an empty object if not passed and return the default rate of 1000 rpm', () => {
const rateLimitSpy = jest.spyOn(AsyncSema, 'RateLimit');

getMatchingRateLimiter('endpoint');

expect(rateLimitSpy).toHaveBeenCalledWith(1000);
});

it('should return the most specific rate limit', () => {
const rateLimiter = 10;
const matchingRateLimiter = getMatchingRateLimiter('endpoint', {
'*': jest.fn(),
'endpoint/*': rateLimiter,
});

expect(matchingRateLimiter).toBe(rateLimiter);
});

it('should return a default rate limit of 1000 rpm if nothing matches', () => {
const rateLimitSpy = jest.spyOn(AsyncSema, 'RateLimit');

getMatchingRateLimiter('endpoint', { someurl: jest.fn() });

expect(rateLimitSpy).toHaveBeenCalledWith(1000);
});

it('should handle expanded rate limit options with a particular method and limit', () => {
const rateLimiter = 10;
const matchingRateLimiter = getMatchingRateLimiter('endpoint', {
'*': { method: 'get', limit: rateLimiter },
});

expect(matchingRateLimiter).toBe(rateLimiter);
});

it('should handle simple rate limit options with a particular limit', () => {
const rateLimiter = 10;
const matchingRateLimiter = getMatchingRateLimiter('endpoint', { '*': rateLimiter });

expect(matchingRateLimiter).toBe(rateLimiter);
});
});
7 changes: 2 additions & 5 deletions packages/rest/src/Requester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import type {
ResourceOptions,
ResponseBodyTypes,
} from '@gitbeaker/requester-utils';
import {
createRequesterFn,
getMatchingRateLimit,
} from '@gitbeaker/requester-utils';
import { createRequesterFn, getMatchingRateLimiter } from '@gitbeaker/requester-utils';

export async function defaultOptionsHandler(
resourceOptions: ResourceOptions,
Expand Down Expand Up @@ -97,7 +94,7 @@ export async function defaultRequestHandler(endpoint: string, options?: RequestO
const retryCodes = [429, 502];
const maxRetries = 10;
const { prefixUrl, asStream, searchParams, rateLimiters, method, ...opts } = options || {};
const endpointRateLimit = getMatchingRateLimit(endpoint, method, rateLimiters);
const endpointRateLimit = getMatchingRateLimiter(endpoint, rateLimiters, method);
let baseUrl: string | undefined;

if (prefixUrl) baseUrl = prefixUrl.endsWith('/') ? prefixUrl : `${prefixUrl}/`;
Expand Down

0 comments on commit 1f0ce18

Please sign in to comment.