Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { beforeEach, describe, expect, test } from 'vitest';
import { createMemoryCache, createNullCache } from '../../cache';
import { createNullLogger } from '../../logger';
import { createTransporter } from '../../transporter';
import type { AlgoliaAgent } from '../../types';

describe('transporter cache', () => {
let requestCount: number;
beforeEach(() => {
requestCount = 0;
});

const algoliaAgent: AlgoliaAgent = {
value: 'test',
add: () => algoliaAgent,
};

const transporter = createTransporter({
hosts: [{ url: 'localhost', accept: 'readWrite', protocol: 'https' }],
hostsCache: createNullCache(),
baseHeaders: {},
baseQueryParameters: {},
algoliaAgent,
logger: createNullLogger(),
timeouts: {
connect: 1000,
read: 2000,
write: 3000,
},
requester: {
send: async () => {
requestCount++;
return {
status: 200,
content: JSON.stringify({ value: requestCount }),
isTimedOut: false,
};
},
},
requestsCache: createMemoryCache(),
responsesCache: createMemoryCache(),
});

test('uses cache for cacheable requests', async () => {
const firstResponse = await transporter.request<{ value: number }>(
{ method: 'GET', path: '/test-1', queryParameters: {}, headers: {}, cacheable: true },
{},
);
const secondResponse = await transporter.request<{ value: number }>(
{ method: 'GET', path: '/test-1', queryParameters: {}, headers: {}, cacheable: true },
{},
);

// Should use cached response, so both values are the same and only 1 request made
expect(firstResponse).toEqual({ value: 1 });
expect(secondResponse).toEqual({ value: 1 });
expect(requestCount).toBe(1);
});

test('does not use cache for implicit non-cacheable requests', async () => {
const firstResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-2', queryParameters: {}, headers: {} },
{},
);
const secondResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-2', queryParameters: {}, headers: {} },
{},
);

// Should NOT use cache, so each request increments the counter
expect(firstResponse).toEqual({ value: 1 });
expect(secondResponse).toEqual({ value: 2 });
expect(requestCount).toBe(2);
});

test('does not use cache for explicit non-cacheable requests', async () => {
const firstResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-3', queryParameters: {}, headers: {}, cacheable: false },
{},
);
const secondResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-3', queryParameters: {}, headers: {}, cacheable: false },
{},
);

// Should NOT use cache, so each request increments the counter
expect(firstResponse).toEqual({ value: 1 });
expect(secondResponse).toEqual({ value: 2 });
expect(requestCount).toBe(2);
});

test('uses cache for POST requests marked as cacheable', async () => {
const firstResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-4', queryParameters: {}, headers: {}, cacheable: true },
{},
);
const secondResponse = await transporter.request<{ value: number }>(
{ method: 'POST', path: '/test-4', queryParameters: {}, headers: {}, cacheable: true },
{},
);

// Should use cached response, so both values are the same and only 1 request made
expect(firstResponse).toEqual({ value: 1 });
expect(secondResponse).toEqual({ value: 1 });
expect(requestCount).toBe(1);
});

test('accepts cacheable from request options', async () => {
const firstResponse = await transporter.request<{ value: number }>(
{ method: 'GET', path: '/test-5', queryParameters: {}, headers: {}, cacheable: false },
{ cacheable: true },
);
const secondResponse = await transporter.request<{ value: number }>(
{ method: 'GET', path: '/test-5', queryParameters: {}, headers: {}, cacheable: false },
{ cacheable: true },
);

// Should use cached response, so both values are the same and only 1 request made
expect(firstResponse).toEqual({ value: 1 });
expect(secondResponse).toEqual({ value: 1 });
expect(requestCount).toBe(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function createTransporter({
async function retryableRequest<TResponse>(
request: Request,
requestOptions: RequestOptions,
isRead = true,
isRead: boolean,
): Promise<TResponse> {
const stackTrace: StackFrame[] = [];

Expand Down Expand Up @@ -211,28 +211,21 @@ export function createTransporter({
}

function createRequest<TResponse>(request: Request, requestOptions: RequestOptions = {}): Promise<TResponse> {
/**
* A read request is either a `GET` request, or a request that we make
* via the `read` transporter (e.g. `search`).
*/
const isRead = request.useReadTransporter || request.method === 'GET';
if (!isRead) {
/**
* On write requests, no cache mechanisms are applied, and we
* proxy the request immediately to the requester.
*/
return retryableRequest<TResponse>(request, requestOptions, isRead);
}

const createRetryableRequest = (): Promise<TResponse> => {
/**
* Then, we prepare a function factory that contains the construction of
* the retryable request. At this point, we may *not* perform the actual
* request. But we want to have the function factory ready.
*/
return retryableRequest<TResponse>(request, requestOptions);
return retryableRequest<TResponse>(request, requestOptions, isRead);
};

/**
* A read request is either a `GET` request, or a request that we make
* via the `read` transporter (e.g. `search`).
*/
const isRead = request.useReadTransporter || request.method === 'GET';

/**
* Once we have the function factory ready, we need to determine of the
* request is "cacheable" - should be cached. Note that, once again,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineConfig({
'src/__tests__/cache/memory-cache.test.ts',
'src/__tests__/create-iterable-promise.test.ts',
'src/__tests__/logger/null-logger.test.ts',
'src/__tests__/transporter/cache.test.ts',
],
name: 'node',
environment: 'node',
Expand All @@ -23,6 +24,7 @@ export default defineConfig({
'src/__tests__/cache/null-cache.test.ts',
'src/__tests__/create-iterable-promise.test.ts',
'src/__tests__/logger/null-logger.test.ts',
'src/__tests__/transporter/cache.test.ts',
],
name: 'jsdom',
environment: 'jsdom',
Expand Down
Loading