-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(backend): capture context creation error (#2805)
Motivation ---------- Apollo server in more recent versions has a number of hooks that get called. E.g. `requestDidStart.didEncounterErrors` hook would not be called if the context creation already failed. I also added `startupDidFail` for convenience. I did not (yet) add `invalidRequestWasReceived` or `didEncounterSubsequentErrors`. See the Apollo reference: https://www.apollographql.com/docs/apollo-server/integrations/plugins-event-reference How to test ----------- 1. `npm run test:unit -- --no-coverage src/server/sentry/index.setupExpress.spec.ts` 2. Tests pass fix: #2644
- Loading branch information
1 parent
8fae51c
commit 4ec78e2
Showing
3 changed files
with
108 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import express from 'express' | ||
import { ApolloServer } from '@apollo/server' | ||
import { expressMiddleware } from '@apollo/server/express4' | ||
import express, { json } from 'express' | ||
import sentryTestkit from 'sentry-testkit' | ||
import supertest from 'supertest' | ||
import { buildSchema, Resolver, Query } from 'type-graphql' | ||
import waitForExpect from 'wait-for-expect' | ||
|
||
import { setupSentry } from '.' | ||
|
@@ -11,31 +14,74 @@ const tk = sentryTestkit() | |
const testkit = tk.testkit | ||
const transport = tk.sentryTransport as () => Transport | ||
|
||
describe('setupSentry.setupExpress', () => { | ||
beforeEach(() => testkit.reset()) | ||
|
||
const createExpressApp = (setupExpress: ReturnType<typeof setupSentry>['setupExpress']) => { | ||
const app = express() | ||
app.get('/crash-me', () => { | ||
throw new Error('Congrats, you crashed me!') | ||
}) | ||
setupExpress(app) | ||
return app | ||
@Resolver() | ||
class ExampleResolver { | ||
@Query(() => Boolean) | ||
successFullQuery(): boolean { | ||
return true | ||
} | ||
} | ||
|
||
describe('setupSentry', () => { | ||
beforeEach(() => testkit.reset()) | ||
|
||
describe('any unhandled error', () => { | ||
let setup: ReturnType<typeof setupSentry>['setupExpress'] | ||
let setup: ReturnType<typeof setupSentry> | ||
|
||
beforeAll(() => { | ||
const dsn = | ||
'https://[email protected]/4508065015922688' | ||
setup = setupSentry({ dsn, transport }).setupExpress | ||
setup = setupSentry({ dsn, transport }) | ||
}) | ||
|
||
it('sends the error to Sentry', async () => { | ||
const app = createExpressApp(setup) | ||
await supertest(app).get('/crash-me').expect(500) | ||
await waitForExpect(() => expect(testkit.reports().length).toBeGreaterThan(0)) | ||
expect(testkit.findReport(new Error('Congrats, you crashed me!'))).toBeDefined() | ||
describe('setupExpress', () => { | ||
const createExpressApp = () => { | ||
const app = express() | ||
app.get('/crash-me', () => { | ||
throw new Error('Congrats, you crashed me!') | ||
}) | ||
setup.setupExpress(app) | ||
return app | ||
} | ||
|
||
it('ensures that any errors from request handlers are sent to Sentry', async () => { | ||
const app = createExpressApp() | ||
await supertest(app).get('/crash-me').expect(500) | ||
await waitForExpect(() => expect(testkit.reports().length).toBeGreaterThan(0)) | ||
expect(testkit.findReport(new Error('Congrats, you crashed me!'))).toBeDefined() | ||
}) | ||
}) | ||
|
||
describe('apolloPlugin', () => { | ||
const createExpressApolloServer = async () => { | ||
const app = express() | ||
const schema = await buildSchema({ | ||
resolvers: [ExampleResolver], | ||
}) | ||
const apolloServer = new ApolloServer<never>({ | ||
schema, | ||
plugins: [setup.apolloPlugin], | ||
}) | ||
await apolloServer.start() | ||
const context = () => { | ||
throw new Error('Oh no! Error during context creation!') | ||
} | ||
app.use(json()) | ||
app.use(expressMiddleware(apolloServer, { context })) | ||
setup.setupExpress(app) | ||
return app | ||
} | ||
|
||
it('sends errors on apollo server context creation to Sentry', async () => { | ||
const app = await createExpressApolloServer() | ||
await supertest(app) | ||
.post('/graphql') | ||
.send({ query: '{ successFullQuery }' }) | ||
.set('Content-Type', 'application/json') | ||
.expect(500) | ||
await waitForExpect(() => expect(testkit.reports().length).toBeGreaterThan(0)) | ||
expect(testkit.findReport(new Error('Oh no! Error during context creation!'))).toBeDefined() | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters