move ApolloServer tests into integration tests from core

This commit is contained in:
Evans Hauser 2018-06-14 01:12:50 -07:00 committed by David Glasser
parent 304debf9ee
commit 9d853f4be4
8 changed files with 971 additions and 989 deletions

View file

@ -1,957 +0,0 @@
/* tslint:disable:no-unused-expression */
import { expect } from 'chai';
import { stub } from 'sinon';
import http from 'http';
import url from 'url';
import 'mocha';
import { sha256 } from 'js-sha256';
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLError,
ValidationContext,
FieldDefinitionNode,
} from 'graphql';
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import WebSocket from 'ws';
import { execute } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import {
createPersistedQueryLink as createPersistedQuery,
VERSION,
} from 'apollo-link-persisted-queries';
import { createApolloFetch } from 'apollo-fetch';
import { ApolloServerBase } from './ApolloServer';
import { AuthenticationError } from './errors';
import { gql } from './index';
import { convertNodeHttpToRequest } from './nodeHttpToRequest';
import { runHttpQuery } from './runHttpQuery';
const INTROSPECTION_QUERY = `
{
__schema {
directives {
name
}
}
}
`;
const TEST_STRING_QUERY = `
{
testString
}
`;
const queryType = new GraphQLObjectType({
name: 'QueryType',
fields: {
testString: {
type: GraphQLString,
resolve() {
return 'test string';
},
},
},
});
const schema = new GraphQLSchema({
query: queryType,
});
function createHttpServer(server: ApolloServerBase) {
return http.createServer(async (req, res) => {
let body: any = [];
req
.on('data', chunk => {
body.push(chunk);
})
.on('end', () => {
body = Buffer.concat(body).toString();
// At this point, we have the headers, method, url and body, and can now
// do whatever we need to in order to respond to this request.
runHttpQuery([req, res], {
method: req.method,
options: (server as any).graphQLServerOptions({ req, res }),
query:
req.method.toUpperCase() === 'GET'
? url.parse(req.url, true)
: JSON.parse(body),
request: convertNodeHttpToRequest(req),
})
.then(gqlResponse => {
res.setHeader('Content-Type', 'application/json');
res.setHeader(
'Content-Length',
Buffer.byteLength(gqlResponse, 'utf8').toString(),
);
res.write(gqlResponse);
res.end();
})
.catch(error => {
if (error.headers) {
Object.keys(error.headers).forEach(header => {
res.setHeader(header, error.headers[header]);
});
}
res.statusCode = error.statusCode;
res.write(error.message);
res.end();
});
});
});
}
describe('ApolloServerBase', () => {
describe('constructor', () => {
describe('validation rules', () => {
it('accepts additional rules', async () => {
const NoTestString = (context: ValidationContext) => ({
Field(node: FieldDefinitionNode) {
if (node.name.value === 'testString') {
context.reportError(
new GraphQLError('Not allowed to use', [node]),
);
}
},
});
const server = new ApolloServerBase({
schema,
validationRules: [NoTestString],
introspection: false,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const introspectionResult = await apolloFetch({
query: INTROSPECTION_QUERY,
});
expect(introspectionResult.data, 'data should not exist').not.to.exist;
expect(introspectionResult.errors, 'errors should exist').to.exist;
const result = await apolloFetch({ query: TEST_STRING_QUERY });
expect(result.data, 'data should not exist').not.to.exist;
expect(result.errors, 'errors should exist').to.exist;
await server.stop();
});
it('allows introspection by default', async () => {
const nodeEnv = process.env.NODE_ENV;
delete process.env.NODE_ENV;
const server = new ApolloServerBase({
schema,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').to.exist;
expect(result.errors, 'errors should exist').not.to.exist;
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
it('prevents introspection by default during production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const server = new ApolloServerBase({
schema,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').not.to.exist;
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal(
'GRAPHQL_VALIDATION_FAILED',
);
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
it('allows introspection to be enabled explicitly', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const server = new ApolloServerBase({
schema,
introspection: true,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').to.exist;
expect(result.errors, 'errors should exist').not.to.exist;
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
});
describe('schema creation', () => {
it('accepts typeDefs and resolvers', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
const server = new ApolloServerBase({
typeDefs,
resolvers,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'hi' });
expect(result.errors, 'errors should exist').not.to.exist;
await server.stop();
});
it('throws if typeDefs are a string', async () => {
const typeDefs: any = `
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
expect(
() =>
new ApolloServerBase({
typeDefs,
resolvers,
}),
).to.throw(/apollo-server/);
});
it('uses schema over resolvers + typeDefs', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
const server = new ApolloServerBase({
typeDefs,
resolvers,
schema,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const typeDefResult = await apolloFetch({ query: '{hello}' });
expect(typeDefResult.data, 'data should not exist').not.to.exist;
expect(typeDefResult.errors, 'errors should exist').to.exist;
const result = await apolloFetch({ query: '{testString}' });
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors, 'errors should exist').not.to.exist;
await server.stop();
});
it('allows mocks as boolean', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const server = new ApolloServerBase({
typeDefs,
mocks: true,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'Hello World' });
expect(result.errors, 'errors should exist').not.to.exist;
await server.stop();
});
it('allows mocks as an object', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const server = new ApolloServerBase({
typeDefs,
mocks: { String: () => 'mock city' },
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'mock city' });
expect(result.errors, 'errors should exist').not.to.exist;
await server.stop();
});
});
});
describe('lifecycle', () => {
it('defers context eval with thunk until after options creation', async () => {
const uniqueContext = { key: 'major' };
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: (_parent, _args, context) => {
expect(context).to.equal(Promise.resolve(uniqueContext));
return 'hi';
},
},
};
const spy = stub().returns({});
const server = new ApolloServerBase({
typeDefs,
resolvers,
context: spy,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
expect(spy.notCalled).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledOnce).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledTwice).true;
await server.stop();
});
it('allows context to be async function', async () => {
const uniqueContext = { key: 'major' };
const spy = stub().returns('hi');
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: (_parent, _args, context) => {
expect(context).to.equal(uniqueContext);
return spy();
},
},
};
const server = new ApolloServerBase({
typeDefs,
resolvers,
context: async () => uniqueContext,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
expect(spy.notCalled).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledOnce).true;
await server.stop();
});
it('returns thrown context error as a valid graphql result', async () => {
const nodeEnv = process.env.NODE_ENV;
delete process.env.NODE_ENV;
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => {
throw Error('never get here');
},
},
};
const server = new ApolloServerBase({
typeDefs,
resolvers,
context: () => {
throw new AuthenticationError('valid result');
},
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.errors.length).to.equal(1);
expect(result.data).not.to.exist;
const e = result.errors[0];
expect(e.message).to.contain('valid result');
expect(e.extensions).to.exist;
expect(e.extensions.code).to.equal('UNAUTHENTICATED');
expect(e.extensions.exception.stacktrace).to.exist;
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
it('propogates error codes in production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const server = new ApolloServerBase({
typeDefs: gql`
type Query {
error: String
}
`,
resolvers: {
Query: {
error: () => {
throw new AuthenticationError('we the best music');
},
},
},
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` });
expect(result.data).to.exist;
expect(result.data).to.deep.equal({ error: null });
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal('UNAUTHENTICATED');
expect(result.errors[0].extensions.exception).not.to.exist;
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
it('propogates error codes with null response in production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const server = new ApolloServerBase({
typeDefs: gql`
type Query {
error: String!
}
`,
resolvers: {
Query: {
error: () => {
throw new AuthenticationError('we the best music');
},
},
},
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` });
expect(result.data).null;
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal('UNAUTHENTICATED');
expect(result.errors[0].extensions.exception).not.to.exist;
process.env.NODE_ENV = nodeEnv;
await server.stop();
});
});
describe('subscriptions', () => {
const SOMETHING_CHANGED_TOPIC = 'something_changed';
const pubsub = new PubSub();
let server: ApolloServerBase;
let subscription;
function createEvent(num) {
return setTimeout(
() =>
pubsub.publish(SOMETHING_CHANGED_TOPIC, {
num,
}),
num + 10,
);
}
afterEach(async () => {
if (server) {
try {
await server.stop();
} catch (e) {}
server = null;
}
if (subscription) {
try {
await subscription.unsubscribe();
} catch (e) {}
subscription = null;
}
});
it('enables subscriptions by default', done => {
const typeDefs = gql`
type Query {
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
createEvent(2);
createEvent(3);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
server = new ApolloServerBase({
typeDefs,
resolvers,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
server.listen({}).then(({ port }) => {
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(data.num).to.equal(i);
if (i === 3) {
done();
}
i++;
} catch (e) {
done(e);
}
},
error: done,
complete: () => {
done(new Error('should not complete'));
},
});
});
});
it('disables subscritpions when option set to false', done => {
const typeDefs = gql`
type Query {
"graphql-js forces there to be a query type"
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
server = new ApolloServerBase({
typeDefs,
resolvers,
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
server
.listen({
subscriptions: false,
})
.then(({ port }) => {
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
subscription = observable.subscribe({
next: () => {
done(new Error('should not call next'));
},
error: () => {
done(new Error('should not notify of error'));
},
complete: () => {
done(new Error('should not complete'));
},
});
//Unfortunately the error connection is not propagated to the
//observable. What should happen is we provide a default onError
//function that notifies the returned observable and can cursomize
//the behavior with an option in the client constructor. If you're
//available to make a PR to the following please do!
//https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts
client.onError((_: Error) => {
done();
});
});
});
it('accepts subscriptions configuration', done => {
const onConnect = stub().callsFake(connectionParams => ({
...connectionParams,
}));
const typeDefs = gql`
type Query {
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
createEvent(2);
createEvent(3);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
server = new ApolloServerBase({
typeDefs,
resolvers,
});
const httpServer = createHttpServer(server);
const path = '/sub';
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
server
.listen({
subscriptions: { onConnect, path },
})
.then(({ port }) => {
expect(onConnect.notCalled).true;
expect(server.subscriptionsPath).to.equal(path);
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(onConnect.calledOnce).true;
expect(data.num).to.equal(i);
if (i === 3) {
done();
}
i++;
} catch (e) {
done(e);
}
},
error: done,
complete: () => {
done(new Error('should not complete'));
},
});
})
.catch(done);
});
});
describe('Persisted Queries', () => {
let server;
const query = gql`
${TEST_STRING_QUERY}
`;
const hash = sha256
.create()
.update(TEST_STRING_QUERY)
.hex();
const extensions = {
persistedQuery: {
version: VERSION,
sha256Hash: hash,
},
};
let uri: string;
beforeEach(async () => {
server = new ApolloServerBase({
schema,
introspection: false,
persistedQueries: {
cache: new Map<string, string>() as any,
},
});
const httpServer = createHttpServer(server);
server.use({
getHttp: () => httpServer,
path: '/graphql',
});
uri = (await server.listen()).url;
});
afterEach(async () => {
await server.stop();
});
it('returns PersistedQueryNotFound on the first try', async () => {
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({
extensions,
} as any);
expect(result.data).not.to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].message).to.equal('PersistedQueryNotFound');
expect(result.errors[0].extensions.code).to.equal(
'PERSISTED_QUERY_NOT_FOUND',
);
});
it('returns result on the second try', async () => {
const apolloFetch = createApolloFetch({ uri });
await apolloFetch({
extensions,
} as any);
const result = await apolloFetch({
extensions,
query: TEST_STRING_QUERY,
} as any);
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors).not.to.exist;
});
it('returns result on the persisted query', async () => {
const apolloFetch = createApolloFetch({ uri });
await apolloFetch({
extensions,
} as any);
await apolloFetch({
extensions,
query: TEST_STRING_QUERY,
} as any);
const result = await apolloFetch({
extensions,
} as any);
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors).not.to.exist;
});
it('returns error when hash does not match', async () => {
const apolloFetch = createApolloFetch({ uri });
try {
await apolloFetch({
extensions: {
persistedQuery: {
version: VERSION,
sha:
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
},
},
query: TEST_STRING_QUERY,
} as any);
expect.fail();
} catch (e) {
expect(e.response.status).to.equal(400);
expect(e.response.raw).to.match(/does not match query/);
}
});
it('returns correct result for persisted query link', done => {
const variables = { id: 1 };
const link = createPersistedQuery().concat(
createHttpLink({ uri, fetch } as any),
);
execute(link, { query, variables } as any).subscribe(result => {
expect(result.data).to.deep.equal({ testString: 'test string' });
done();
}, done);
});
it('returns correct result for persisted query link using get request', done => {
const variables = { id: 1 };
const link = createPersistedQuery({
useGETForHashedQueries: true,
}).concat(createHttpLink({ uri, fetch } as any));
execute(link, { query, variables } as any).subscribe(result => {
expect(result.data).to.deep.equal({ testString: 'test string' });
done();
}, done);
});
});
});

View file

@ -2,6 +2,9 @@ import { expect } from 'chai';
import 'mocha'; import 'mocha';
import express from 'express'; import express from 'express';
import net from 'net';
import http from 'http';
import request from 'request'; import request from 'request';
import FormData from 'form-data'; import FormData from 'form-data';
import fs from 'fs'; import fs from 'fs';
@ -9,7 +12,12 @@ import fetch from 'node-fetch';
import { createApolloFetch } from 'apollo-fetch'; import { createApolloFetch } from 'apollo-fetch';
import { ApolloServerBase, AuthenticationError } from 'apollo-server-core'; import { ApolloServerBase, AuthenticationError } from 'apollo-server-core';
import { registerServer } from './ApolloServer'; import { registerServer, ApolloServer } from './ApolloServer';
import {
testApolloServer,
createServerInfo,
} from 'apollo-server-integration-testsuite';
//to remove the circular dependency, we reference it directly //to remove the circular dependency, we reference it directly
const gql = require('../../apollo-server/dist/index').gql; const gql = require('../../apollo-server/dist/index').gql;
@ -26,22 +34,43 @@ const resolvers = {
}, },
}; };
const url = 'http://localhost:4000/graphql';
const uri = url;
describe('apollo-server-express', () => {
let server;
let httpServer;
testApolloServer(
async options => {
server = new ApolloServer(options);
const app = express();
registerServer({ app, server });
httpServer = await new Promise<http.Server>(resolve => {
const s = app.listen({ port: 4000 }, () => resolve(s));
});
return createServerInfo(server, httpServer);
},
async () => {
if (server) await server.stop();
if (httpServer && httpServer.listening) await httpServer.close();
},
);
});
describe('apollo-server-express', () => { describe('apollo-server-express', () => {
//to remove the circular dependency, we reference it directly //to remove the circular dependency, we reference it directly
const ApolloServer = require('../../apollo-server/dist/index').ApolloServer; const ApolloServer = require('../../apollo-server/dist/index').ApolloServer;
let server: ApolloServerBase & { let server: ApolloServerBase | any;
createGraphQLServerOptions: (
req: express.Request,
res: express.Response,
) => any;
};
let app: express.Application; let app: express.Application;
let httpServer: http.Server;
afterEach(async () => { afterEach(async () => {
if (server) await server.stop(); if (server) await server.stop();
if (httpServer) await httpServer.close();
}); });
describe('', () => { describe('constructor', () => {
it('accepts typeDefs and resolvers', () => { it('accepts typeDefs and resolvers', () => {
const app = express(); const app = express();
const server = new ApolloServer({ typeDefs, resolvers }); const server = new ApolloServer({ typeDefs, resolvers });
@ -65,7 +94,9 @@ describe('apollo-server-express', () => {
registerServer({ app, server }); registerServer({ app, server });
const { url: uri } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' }); const result = await apolloFetch({ query: '{hello}' });
@ -96,7 +127,9 @@ describe('apollo-server-express', () => {
registerServer({ app, server, gui: true }); registerServer({ app, server, gui: true });
const { url } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri: url }); const apolloFetch = createApolloFetch({ uri: url });
const result = await apolloFetch({ query: INTROSPECTION_QUERY }); const result = await apolloFetch({ query: INTROSPECTION_QUERY });
@ -105,7 +138,7 @@ describe('apollo-server-express', () => {
'GRAPHQL_VALIDATION_FAILED', 'GRAPHQL_VALIDATION_FAILED',
); );
return new Promise((resolve, reject) => { return new Promise<http.Server>((resolve, reject) => {
request( request(
{ {
url, url,
@ -140,8 +173,10 @@ describe('apollo-server-express', () => {
registerServer({ app, server }); registerServer({ app, server });
const { url } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
return new Promise((resolve, reject) => { const l = app.listen({ port: 4000 }, () => resolve(l));
});
return new Promise<http.Server>((resolve, reject) => {
request( request(
{ {
url, url,
@ -174,7 +209,9 @@ describe('apollo-server-express', () => {
registerServer({ app, server, cors: { origin: 'apollographql.com' } }); registerServer({ app, server, cors: { origin: 'apollographql.com' } });
const { url: uri } = await server.listen({}); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }).useAfter( const apolloFetch = createApolloFetch({ uri }).useAfter(
(response, next) => { (response, next) => {
@ -196,7 +233,9 @@ describe('apollo-server-express', () => {
registerServer({ app, server, bodyParserConfig: { limit: 0 } }); registerServer({ app, server, bodyParserConfig: { limit: 0 } });
const { url: uri } = await server.listen({}); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
@ -226,7 +265,10 @@ describe('apollo-server-express', () => {
registerServer({ app, server, bodyParserConfig: { limit: 0 } }); registerServer({ app, server, bodyParserConfig: { limit: 0 } });
const { port } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const { port } = httpServer.address() as net.AddressInfo;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request( request(
@ -262,7 +304,10 @@ describe('apollo-server-express', () => {
}, },
}); });
const { port } = await server.listen({}); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const { port } = httpServer.address() as net.AddressInfo;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request( request(
@ -295,7 +340,10 @@ describe('apollo-server-express', () => {
disableHealthCheck: true, disableHealthCheck: true,
}); });
const { port } = await server.listen({}); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const { port } = httpServer.address() as net.AddressInfo;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request( request(
@ -351,7 +399,10 @@ describe('apollo-server-express', () => {
server, server,
}); });
const { port } = await server.listen({}); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const { port } = httpServer.address() as net.AddressInfo;
const body = new FormData(); const body = new FormData();
@ -423,7 +474,9 @@ describe('apollo-server-express', () => {
app = express(); app = express();
registerServer({ app, server }); registerServer({ app, server });
const { url: uri } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' }); const result = await apolloFetch({ query: '{hello}' });
@ -461,7 +514,9 @@ describe('apollo-server-express', () => {
app = express(); app = express();
registerServer({ app, server }); registerServer({ app, server });
const { url: uri } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` }); const result = await apolloFetch({ query: `{error}` });
@ -499,7 +554,9 @@ describe('apollo-server-express', () => {
app = express(); app = express();
registerServer({ app, server }); registerServer({ app, server });
const { url: uri } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` }); const result = await apolloFetch({ query: `{error}` });
@ -536,7 +593,9 @@ describe('apollo-server-express', () => {
app = express(); app = express();
registerServer({ app, server }); registerServer({ app, server });
const { url: uri } = await server.listen(); httpServer = await new Promise<http.Server>(resolve => {
const l = app.listen({ port: 4000 }, () => resolve(l));
});
const apolloFetch = createApolloFetch({ uri }); const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` }); const result = await apolloFetch({ query: `{error}` });

View file

@ -0,0 +1,28 @@
import { Server } from 'hapi';
import {
testApolloServer,
createServerInfo,
} from 'apollo-server-integration-testsuite';
import { registerServer, ApolloServer } from './ApolloServer';
describe('apollo-server-hapi', () => {
let server;
let app;
let httpServer;
testApolloServer(
async options => {
server = new ApolloServer(options);
app = new Server({ host: 'localhost', port: 4000 });
registerServer({ app, server });
await app.start();
const httpServer = app.listener;
return createServerInfo(server, httpServer);
},
async () => {
if (server) await server.stop();
if (app) await app.stop();
if (httpServer && httpServer.listening) await httpServer.close();
},
);
});

View file

@ -31,8 +31,14 @@
"definition": "dist/index.d.ts" "definition": "dist/index.d.ts"
}, },
"devDependencies": { "devDependencies": {
"apollo-link-persisted-queries": "^0.2.0", "apollo-fetch": "^0.7.0",
"apollo-link": "^1.2.2",
"apollo-link-http": "^1.5.4",
"apollo-link-persisted-queries": "^0.2.1",
"graphql-subscriptions": "^0.5.8",
"graphql-tag": "^2.9.2", "graphql-tag": "^2.9.2",
"js-sha256": "^0.9.0" "js-sha256": "^0.9.0",
"subscriptions-transport-ws": "^0.9.11",
"ws": "^5.2.0"
} }
} }

View file

@ -0,0 +1,846 @@
/* tslint:disable:no-unused-expression */
import { expect } from 'chai';
import { stub } from 'sinon';
import http from 'http';
import net from 'net';
import 'mocha';
import { sha256 } from 'js-sha256';
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLError,
ValidationContext,
FieldDefinitionNode,
} from 'graphql';
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import WebSocket from 'ws';
import { execute } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import {
createPersistedQueryLink as createPersistedQuery,
VERSION,
} from 'apollo-link-persisted-queries';
import { createApolloFetch } from 'apollo-fetch';
import {
AuthenticationError,
gql,
Config,
ApolloServerBase,
} from 'apollo-server-core';
export function createServerInfo<AS extends ApolloServerBase>(
server: AS,
httpServer: http.Server,
): ServerInfo<AS> {
const serverInfo: any = {
...(httpServer.address() as net.AddressInfo),
server,
httpServer,
};
// Convert IPs which mean "any address" (IPv4 or IPv6) into localhost
// corresponding loopback ip. Note that the url field we're setting is
// primarily for consumption by our test suite. If this heuristic is
// wrong for your use case, explicitly specify a frontend host (in the
// `frontends.host` field in your engine config, or in the `host`
// option to ApolloServer.listen).
let hostForUrl = serverInfo.address;
if (serverInfo.address === '' || serverInfo.address === '::')
hostForUrl = 'localhost';
serverInfo.url = require('url').format({
protocol: 'http',
hostname: hostForUrl,
port: serverInfo.port,
pathname: server.graphqlPath,
});
return serverInfo;
}
const INTROSPECTION_QUERY = `
{
__schema {
directives {
name
}
}
}
`;
const TEST_STRING_QUERY = `
{
testString
}
`;
const queryType = new GraphQLObjectType({
name: 'QueryType',
fields: {
testString: {
type: GraphQLString,
resolve() {
return 'test string';
},
},
},
});
const schema = new GraphQLSchema({
query: queryType,
});
export interface ServerInfo<AS extends ApolloServerBase> {
address: string;
family: string;
url: string;
port: number | string;
server: AS;
httpServer: http.Server;
}
export interface CreateServerFunc<AS extends ApolloServerBase> {
(config: Config): Promise<ServerInfo<AS>>;
}
export interface StopServerFunc {
(): Promise<void>;
}
export function testApolloServer<AS extends ApolloServerBase>(
createApolloServer: CreateServerFunc<AS>,
stopServer: StopServerFunc,
) {
describe('ApolloServer', () => {
afterEach(stopServer);
describe('constructor', () => {
describe('validation rules', () => {
it('accepts additional rules', async () => {
const NoTestString = (context: ValidationContext) => ({
Field(node: FieldDefinitionNode) {
if (node.name.value === 'testString') {
context.reportError(
new GraphQLError('Not allowed to use', [node]),
);
}
},
});
const { url: uri } = await createApolloServer({
schema,
validationRules: [NoTestString],
introspection: false,
});
const apolloFetch = createApolloFetch({ uri });
const introspectionResult = await apolloFetch({
query: INTROSPECTION_QUERY,
});
expect(introspectionResult.data, 'data should not exist').not.to
.exist;
expect(introspectionResult.errors, 'errors should exist').to.exist;
const result = await apolloFetch({ query: TEST_STRING_QUERY });
expect(result.data, 'data should not exist').not.to.exist;
expect(result.errors, 'errors should exist').to.exist;
});
it('allows introspection by default', async () => {
const nodeEnv = process.env.NODE_ENV;
delete process.env.NODE_ENV;
const { url: uri } = await createApolloServer({
schema,
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').to.exist;
expect(result.errors, 'errors should exist').not.to.exist;
process.env.NODE_ENV = nodeEnv;
});
it('prevents introspection by default during production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const { url: uri } = await createApolloServer({
schema,
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').not.to.exist;
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal(
'GRAPHQL_VALIDATION_FAILED',
);
process.env.NODE_ENV = nodeEnv;
});
it('allows introspection to be enabled explicitly', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const { url: uri } = await createApolloServer({
schema,
introspection: true,
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: INTROSPECTION_QUERY });
expect(result.data, 'data should not exist').to.exist;
expect(result.errors, 'errors should exist').not.to.exist;
process.env.NODE_ENV = nodeEnv;
});
});
describe('schema creation', () => {
it('accepts typeDefs and resolvers', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'hi' });
expect(result.errors, 'errors should exist').not.to.exist;
});
it('throws if typeDefs are a string', done => {
const typeDefs: any = `
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
createApolloServer({
typeDefs,
resolvers,
})
.then(expect.fail)
.catch(e => expect(e.message).to.match(/apollo-server/))
.then(() => done());
});
it('uses schema over resolvers + typeDefs', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = { Query: { hello: () => 'hi' } };
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
schema,
});
const apolloFetch = createApolloFetch({ uri });
const typeDefResult = await apolloFetch({ query: '{hello}' });
expect(typeDefResult.data, 'data should not exist').not.to.exist;
expect(typeDefResult.errors, 'errors should exist').to.exist;
const result = await apolloFetch({ query: '{testString}' });
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors, 'errors should exist').not.to.exist;
});
it('allows mocks as boolean', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const { url: uri } = await createApolloServer({
typeDefs,
mocks: true,
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'Hello World' });
expect(result.errors, 'errors should exist').not.to.exist;
});
it('allows mocks as an object', async () => {
const typeDefs = gql`
type Query {
hello: String
}
`;
const { url: uri } = await createApolloServer({
typeDefs,
mocks: { String: () => 'mock city' },
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.data).to.deep.equal({ hello: 'mock city' });
expect(result.errors, 'errors should exist').not.to.exist;
});
});
});
describe('lifecycle', () => {
it('defers context eval with thunk until after options creation', async () => {
const uniqueContext = { key: 'major' };
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: (_parent, _args, context) => {
expect(context).to.equal(Promise.resolve(uniqueContext));
return 'hi';
},
},
};
const spy = stub().returns({});
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
context: spy,
});
const apolloFetch = createApolloFetch({ uri });
expect(spy.notCalled).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledOnce).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledTwice).true;
});
it('allows context to be async function', async () => {
const uniqueContext = { key: 'major' };
const spy = stub().returns('hi');
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: (_parent, _args, context) => {
expect(context).to.equal(uniqueContext);
return spy();
},
},
};
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
context: async () => uniqueContext,
});
const apolloFetch = createApolloFetch({ uri });
expect(spy.notCalled).true;
await apolloFetch({ query: '{hello}' });
expect(spy.calledOnce).true;
});
it('returns thrown context error as a valid graphql result', async () => {
const nodeEnv = process.env.NODE_ENV;
delete process.env.NODE_ENV;
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => {
throw Error('never get here');
},
},
};
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
context: () => {
throw new AuthenticationError('valid result');
},
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: '{hello}' });
expect(result.errors.length).to.equal(1);
expect(result.data).not.to.exist;
const e = result.errors[0];
expect(e.message).to.contain('valid result');
expect(e.extensions).to.exist;
expect(e.extensions.code).to.equal('UNAUTHENTICATED');
expect(e.extensions.exception.stacktrace).to.exist;
process.env.NODE_ENV = nodeEnv;
});
it('propogates error codes in production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const { url: uri } = await createApolloServer({
typeDefs: gql`
type Query {
error: String
}
`,
resolvers: {
Query: {
error: () => {
throw new AuthenticationError('we the best music');
},
},
},
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` });
expect(result.data).to.exist;
expect(result.data).to.deep.equal({ error: null });
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal('UNAUTHENTICATED');
expect(result.errors[0].extensions.exception).not.to.exist;
process.env.NODE_ENV = nodeEnv;
});
it('propogates error codes with null response in production', async () => {
const nodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
const { url: uri } = await createApolloServer({
typeDefs: gql`
type Query {
error: String!
}
`,
resolvers: {
Query: {
error: () => {
throw new AuthenticationError('we the best music');
},
},
},
});
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({ query: `{error}` });
expect(result.data).null;
expect(result.errors, 'errors should exist').to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].extensions.code).to.equal('UNAUTHENTICATED');
expect(result.errors[0].extensions.exception).not.to.exist;
process.env.NODE_ENV = nodeEnv;
});
});
describe('subscriptions', () => {
const SOMETHING_CHANGED_TOPIC = 'something_changed';
const pubsub = new PubSub();
let subscription;
function createEvent(num) {
return setTimeout(
() =>
pubsub.publish(SOMETHING_CHANGED_TOPIC, {
num,
}),
num + 10,
);
}
afterEach(async () => {
if (subscription) {
try {
await subscription.unsubscribe();
} catch (e) {}
subscription = null;
}
});
it('enables subscriptions after creating subscriptions server', done => {
const typeDefs = gql`
type Query {
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
createEvent(2);
createEvent(3);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
createApolloServer({
typeDefs,
resolvers,
}).then(({ port, server, httpServer }) => {
server.createSubscriptionServer(httpServer);
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(data.num).to.equal(i);
if (i === 3) {
done();
}
i++;
} catch (e) {
done(e);
}
},
error: done,
complete: () => {
done(new Error('should not complete'));
},
});
});
});
it('disables subscritpions when option set to false', done => {
const typeDefs = gql`
type Query {
"graphql-js forces there to be a query type"
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
createApolloServer({
typeDefs,
resolvers,
subscriptions: false,
}).then(({ port, server, httpServer }) => {
try {
server.createSubscriptionServer(httpServer);
expect.fail();
} catch (e) {
expect(e.message).to.match(/disabled/);
}
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
subscription = observable.subscribe({
next: () => {
done(new Error('should not call next'));
},
error: () => {
done(new Error('should not notify of error'));
},
complete: () => {
done(new Error('should not complete'));
},
});
//Unfortunately the error connection is not propagated to the
//observable. What should happen is we provide a default onError
//function that notifies the returned observable and can cursomize
//the behavior with an option in the client constructor. If you're
//available to make a PR to the following please do!
//https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts
client.onError((_: Error) => {
done();
});
});
});
it('accepts subscriptions configuration', done => {
const onConnect = stub().callsFake(connectionParams => ({
...connectionParams,
}));
const typeDefs = gql`
type Query {
hi: String
}
type Subscription {
num: Int
}
`;
const query = `
subscription {
num
}
`;
const resolvers = {
Query: {
hi: () => 'here to placate graphql-js',
},
Subscription: {
num: {
subscribe: () => {
createEvent(1);
createEvent(2);
createEvent(3);
return pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC);
},
},
},
};
const path = '/sub';
createApolloServer({
typeDefs,
resolvers,
subscriptions: { onConnect, path },
})
.then(({ port, server, httpServer }) => {
server.createSubscriptionServer(httpServer);
expect(onConnect.notCalled).true;
expect(server.subscriptionsPath).to.equal(path);
const client = new SubscriptionClient(
`ws://localhost:${port}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });
let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(onConnect.calledOnce).true;
expect(data.num).to.equal(i);
if (i === 3) {
done();
}
i++;
} catch (e) {
done(e);
}
},
error: done,
complete: () => {
done(new Error('should not complete'));
},
});
})
.catch(done);
});
});
describe('Persisted Queries', () => {
let uri: string;
const query = gql`
${TEST_STRING_QUERY}
`;
const hash = sha256
.create()
.update(TEST_STRING_QUERY)
.hex();
const extensions = {
persistedQuery: {
version: VERSION,
sha256Hash: hash,
},
};
beforeEach(async () => {
const serverInfo = await createApolloServer({
schema,
introspection: false,
persistedQueries: {
cache: new Map<string, string>() as any,
},
});
uri = serverInfo.url;
});
it('returns PersistedQueryNotFound on the first try', async () => {
const apolloFetch = createApolloFetch({ uri });
const result = await apolloFetch({
extensions,
} as any);
expect(result.data).not.to.exist;
expect(result.errors.length).to.equal(1);
expect(result.errors[0].message).to.equal('PersistedQueryNotFound');
expect(result.errors[0].extensions.code).to.equal(
'PERSISTED_QUERY_NOT_FOUND',
);
});
it('returns result on the second try', async () => {
const apolloFetch = createApolloFetch({ uri });
await apolloFetch({
extensions,
} as any);
const result = await apolloFetch({
extensions,
query: TEST_STRING_QUERY,
} as any);
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors).not.to.exist;
});
it('returns result on the persisted query', async () => {
const apolloFetch = createApolloFetch({ uri });
await apolloFetch({
extensions,
} as any);
await apolloFetch({
extensions,
query: TEST_STRING_QUERY,
} as any);
const result = await apolloFetch({
extensions,
} as any);
expect(result.data).to.deep.equal({ testString: 'test string' });
expect(result.errors).not.to.exist;
});
//Apollo Fetch's result depends on the server implementation, if the
//statusText of the error is unparsable, then we'll fall into the catch,
//such as with express. If it is parsable, then we'll use the afterware
it('returns error when hash does not match', async () => {
const apolloFetch = createApolloFetch({ uri }).useAfter((res, next) => {
expect(res.response.status).to.equal(400);
expect(res.response.raw).to.match(/does not match query/);
next();
});
try {
await apolloFetch({
extensions: {
persistedQuery: {
version: VERSION,
sha:
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
},
},
query: TEST_STRING_QUERY,
} as any);
} catch (e) {
expect(e.response).to.exist;
expect(e.response.status).to.equal(400);
expect(e.response.raw).to.match(/does not match query/);
}
});
it('returns correct result for persisted query link', done => {
const variables = { id: 1 };
const link = createPersistedQuery().concat(
createHttpLink({ uri, fetch } as any),
);
execute(link, { query, variables } as any).subscribe(result => {
expect(result.data).to.deep.equal({ testString: 'test string' });
done();
}, done);
});
it('returns correct result for persisted query link using get request', done => {
const variables = { id: 1 };
const link = createPersistedQuery({
useGETForHashedQueries: true,
}).concat(createHttpLink({ uri, fetch } as any));
execute(link, { query, variables } as any).subscribe(result => {
expect(result.data).to.deep.equal({ testString: 'test string' });
done();
}, done);
});
});
});
}

View file

@ -24,6 +24,8 @@ import { GraphQLOptions, Config } from 'apollo-server-core';
import { OperationStore } from 'apollo-server-module-operation-store'; import { OperationStore } from 'apollo-server-module-operation-store';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
export * from './ApolloServer';
const personType = new GraphQLObjectType({ const personType = new GraphQLObjectType({
name: 'PersonType', name: 'PersonType',
fields: { fields: {

View file

@ -89,7 +89,7 @@ describe('apollo-server', () => {
resolvers, resolvers,
}); });
const { url: uri } = await server.listen({}); const { url: uri } = await server.listen();
const apolloFetch = createApolloFetch({ uri }).useAfter( const apolloFetch = createApolloFetch({ uri }).useAfter(
(response, next) => { (response, next) => {

View file

@ -12,22 +12,20 @@ process.on('unhandledRejection', reason => {
require('../packages/apollo-server-core/dist/runQuery.test.js'); require('../packages/apollo-server-core/dist/runQuery.test.js');
require('../packages/apollo-server-core/dist/runHttpQuery.test.js'); require('../packages/apollo-server-core/dist/runHttpQuery.test.js');
require('../packages/apollo-server-core/dist/errors.test.js'); require('../packages/apollo-server-core/dist/errors.test.js');
require('../packages/apollo-server-core/dist/ApolloServer.test.js');
//Apollo server 2 tests //Apollo server 2 tests
//apollo-server //apollo-server
require('../packages/apollo-server/dist/index.test.js'); require('../packages/apollo-server/dist/index.test.js');
//apollo-server-express
require('../packages/apollo-server-express/dist/ApolloServer.test.js');
//Apollo server 1 tests
require('../packages/apollo-server-module-operation-store/dist/operationStore.test'); require('../packages/apollo-server-module-operation-store/dist/operationStore.test');
require('../packages/apollo-server-express/dist/ApolloServer.test.js');
require('../packages/apollo-server-express/dist/expressApollo.test'); require('../packages/apollo-server-express/dist/expressApollo.test');
require('../packages/apollo-server-express/dist/connectApollo.test'); require('../packages/apollo-server-express/dist/connectApollo.test');
(NODE_MAJOR_VERSION >= 9 || (NODE_MAJOR_VERSION >= 9 ||
(NODE_MAJOR_VERSION >= 8 && NODE_MAJOR_REVISION >= 9)) && (NODE_MAJOR_VERSION >= 8 && NODE_MAJOR_REVISION >= 9)) &&
require('../packages/apollo-server-hapi/dist/hapiApollo.test'); // Hapi 17 is 8.9+ require('../packages/apollo-server-hapi/dist/hapiApollo.test') && // Hapi 17 is 8.9+
require('../packages/apollo-server-hapi/dist/ApolloServer.test.js');
require('../packages/apollo-server-express/dist/apolloServerHttp.test'); require('../packages/apollo-server-express/dist/apolloServerHttp.test');