Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/apollo-cache-control/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"apollo-server-env": "file:../apollo-server-env",
"graphql-extensions": "file:../graphql-extensions"
"apollo-server-plugin-base": "file:../apollo-server-plugin-base"
},
"peerDependencies": {
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
Expand Down

This file was deleted.

171 changes: 171 additions & 0 deletions packages/apollo-cache-control/src/__tests__/cacheControlPlugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { ResponsePath, GraphQLError } from 'graphql';
import { Headers } from 'apollo-server-env';
import {
CacheScope,
CacheControlExtensionOptions,
CacheHint,
__testing__,
plugin,
} from '../';
const { addHint, computeOverallCachePolicy } = __testing__;
import {
GraphQLRequestContextWillSendResponse,
GraphQLResponse,
} from 'apollo-server-plugin-base';
import pluginTestHarness from 'apollo-server-core/dist/utils/pluginTestHarness';

describe('plugin', () => {
describe('willSendResponse', () => {
function makePluginWithOptions({
pluginInitializationOptions,
overallCachePolicy,
errors = false,
}: {
pluginInitializationOptions?: CacheControlExtensionOptions;
overallCachePolicy?: Required<CacheHint>;
errors?: boolean;
} = Object.create(null)) {
const pluginInstance = plugin(pluginInitializationOptions);

return pluginTestHarness({
pluginInstance,
overallCachePolicy,
graphqlRequest: { query: 'does not matter' },
executor: () => {
const response: GraphQLResponse = {
http: {
headers: new Headers(),
},
data: { test: 'test' },
};

if (errors) {
response.errors = [new GraphQLError('Test Error')];
}

return response;
},
});
}

describe('HTTP cache-control header', () => {
const overallCachePolicy: Required<CacheHint> = {
maxAge: 300,
scope: CacheScope.Public,
};

it('is set when calculateHttpHeaders is set to true', async () => {
const requestContext = await makePluginWithOptions({
pluginInitializationOptions: {
calculateHttpHeaders: true,
},
overallCachePolicy,
});
expect(requestContext.response.http!.headers.get('Cache-Control')).toBe(
'max-age=300, public',
);
});

const shouldNotSetCacheControlHeader = (
requestContext: GraphQLRequestContextWillSendResponse<any>,
) => {
expect(
requestContext.response.http!.headers.get('Cache-Control'),
).toBeNull();
};

it('is not set when calculateHttpHeaders is set to false', async () => {
const requestContext = await makePluginWithOptions({
pluginInitializationOptions: {
calculateHttpHeaders: false,
},
overallCachePolicy,
});
shouldNotSetCacheControlHeader(requestContext);
});

it('is not set if response has errors', async () => {
const requestContext = await makePluginWithOptions({
pluginInitializationOptions: {
calculateHttpHeaders: false,
},
overallCachePolicy,
errors: true,
});
shouldNotSetCacheControlHeader(requestContext);
});

it('does not set cache-control header if there is no overall cache policy', async () => {
const requestContext = await makePluginWithOptions({
pluginInitializationOptions: {
calculateHttpHeaders: false,
},
overallCachePolicy: undefined,
errors: true,
});
shouldNotSetCacheControlHeader(requestContext);
});
});
});

describe('computeOverallCachePolicy', () => {
const responsePath: ResponsePath = {
key: 'test',
prev: undefined,
};
const responseSubPath: ResponsePath = {
key: 'subTest',
prev: responsePath,
};
const responseSubSubPath: ResponsePath = {
key: 'subSubTest',
prev: responseSubPath,
};

const hints = new Map();
afterEach(() => hints.clear());

it('returns undefined without cache hints', () => {
const cachePolicy = computeOverallCachePolicy(hints);
expect(cachePolicy).toBeUndefined();
});

it('returns lowest max age value', () => {
addHint(hints, responsePath, { maxAge: 10 });
addHint(hints, responseSubPath, { maxAge: 20 });

const cachePolicy = computeOverallCachePolicy(hints);
expect(cachePolicy).toHaveProperty('maxAge', 10);
});

it('returns undefined if any cache hint has a maxAge of 0', () => {
addHint(hints, responsePath, { maxAge: 120 });
addHint(hints, responseSubPath, { maxAge: 0 });
addHint(hints, responseSubSubPath, { maxAge: 20 });

const cachePolicy = computeOverallCachePolicy(hints);
expect(cachePolicy).toBeUndefined();
});

it('returns PUBLIC scope by default', () => {
addHint(hints, responsePath, { maxAge: 10 });

const cachePolicy = computeOverallCachePolicy(hints);
expect(cachePolicy).toHaveProperty('scope', CacheScope.Public);
});

it('returns PRIVATE scope if any cache hint has PRIVATE scope', () => {
addHint(hints, responsePath, {
maxAge: 10,
scope: CacheScope.Public,
});
addHint(hints, responseSubPath, {
maxAge: 10,
scope: CacheScope.Private,
});

const cachePolicy = computeOverallCachePolicy(hints);
expect(cachePolicy).toHaveProperty('scope', CacheScope.Private);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
import { GraphQLSchema, graphql } from 'graphql';

import {
enableGraphQLExtensions,
GraphQLExtensionStack,
} from 'graphql-extensions';
import {
CacheControlExtension,
CacheHint,
CacheControlExtensionOptions,
plugin,
} from '../';
import pluginTestHarness from 'apollo-server-core/dist/utils/pluginTestHarness';

export async function collectCacheControlHints(
schema: GraphQLSchema,
source: string,
options?: CacheControlExtensionOptions,
): Promise<CacheHint[]> {
enableGraphQLExtensions(schema);

// Because this test helper looks at the formatted extensions, we always want
// to include them.
const cacheControlExtension = new CacheControlExtension({
// to include them in the response rather than allow them to be stripped
// out.
const pluginInstance = plugin({
...options,
stripFormattedExtensions: false,
});

const response = await graphql({
const requestContext = await pluginTestHarness({
pluginInstance,
schema,
source,
contextValue: {
_extensionStack: new GraphQLExtensionStack([cacheControlExtension]),
graphqlRequest: {
query: source,
},
executor: async (requestContext) => {
return await graphql({
schema,
source: requestContext.request.query,
contextValue: requestContext.context,
});
}
});

expect(response.errors).toBeUndefined();
expect(requestContext.response.errors).toBeUndefined();

return cacheControlExtension.format()[1].hints;
return requestContext.response.extensions!.cacheControl.hints;
}
Loading