From 32c6e8bdfad99987960550dcffa80170460eda43 Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Wed, 24 Jul 2024 09:56:37 -0400 Subject: [PATCH 1/2] test!: Updated minimum test version of `apollo-server` and `apollo-server-express` to 3.0.0. Removed testing against `apollo-server-fastify`, `apollo-server-hapi`, `apollo-server-koa`, `apollo-server-lambda` as we found the plugin instrumentation had no effect on the relevant, deprecated pacakges (#315) --- .../apollo-server-express/package.json | 5 +- .../apollo-server-fastify-setup.js | 86 -- .../apollo-server-fastify/attributes.test.js | 11 - .../apollo-server-fastify/errors.test.js | 13 - .../apollo-server-fastify/metrics.test.js | 20 - .../apollo-server-fastify/newrelic.js | 25 - .../apollo-server-fastify/package.json | 27 - .../query-obfuscation.test.js | 11 - .../apollo-server-fastify/segments.test.js | 793 ---------------- .../transaction-naming.test.js | 11 - .../apollo-server-hapi-setup.js | 84 -- .../apollo-server-hapi/attributes.test.js | 11 - .../apollo-server-hapi/errors.test.js | 13 - .../apollo-server-hapi/metrics.test.js | 20 - .../versioned/apollo-server-hapi/newrelic.js | 25 - .../versioned/apollo-server-hapi/package.json | 26 - .../query-obfuscation.test.js | 11 - .../apollo-server-hapi/segments.test.js | 787 ---------------- .../transaction-naming.test.js | 11 - .../apollo-server-koa-setup.js | 91 -- .../apollo-server-koa/attributes.test.js | 11 - .../apollo-server-koa/errors.test.js | 13 - .../apollo-server-koa/metrics.test.js | 20 - tests/versioned/apollo-server-koa/newrelic.js | 25 - .../versioned/apollo-server-koa/package.json | 43 - .../query-obfuscation.test.js | 11 - .../apollo-server-koa/segments.test.js | 787 ---------------- .../transaction-naming.test.js | 11 - .../apollo-server-lambda-setup.js | 104 --- .../apollo-server-lambda/attributes-tests.js | 634 ------------- .../apollo-server-lambda/errors-tests.js | 175 ---- .../apollo-server-lambda/lambda-test-utils.js | 259 ------ .../apollo-server-lambda/newrelic.js | 31 - .../apollo-server-lambda/package.json | 25 - .../query-obfuscation-tests.js | 77 -- .../apollo-server-lambda/segments.test.js | 858 ------------------ .../transaction-naming-tests.js | 506 ----------- tests/versioned/apollo-server/package.json | 5 +- 38 files changed, 8 insertions(+), 5668 deletions(-) delete mode 100644 tests/versioned/apollo-server-fastify/apollo-server-fastify-setup.js delete mode 100644 tests/versioned/apollo-server-fastify/attributes.test.js delete mode 100644 tests/versioned/apollo-server-fastify/errors.test.js delete mode 100644 tests/versioned/apollo-server-fastify/metrics.test.js delete mode 100644 tests/versioned/apollo-server-fastify/newrelic.js delete mode 100644 tests/versioned/apollo-server-fastify/package.json delete mode 100644 tests/versioned/apollo-server-fastify/query-obfuscation.test.js delete mode 100644 tests/versioned/apollo-server-fastify/segments.test.js delete mode 100644 tests/versioned/apollo-server-fastify/transaction-naming.test.js delete mode 100644 tests/versioned/apollo-server-hapi/apollo-server-hapi-setup.js delete mode 100644 tests/versioned/apollo-server-hapi/attributes.test.js delete mode 100644 tests/versioned/apollo-server-hapi/errors.test.js delete mode 100644 tests/versioned/apollo-server-hapi/metrics.test.js delete mode 100644 tests/versioned/apollo-server-hapi/newrelic.js delete mode 100644 tests/versioned/apollo-server-hapi/package.json delete mode 100644 tests/versioned/apollo-server-hapi/query-obfuscation.test.js delete mode 100644 tests/versioned/apollo-server-hapi/segments.test.js delete mode 100644 tests/versioned/apollo-server-hapi/transaction-naming.test.js delete mode 100644 tests/versioned/apollo-server-koa/apollo-server-koa-setup.js delete mode 100644 tests/versioned/apollo-server-koa/attributes.test.js delete mode 100644 tests/versioned/apollo-server-koa/errors.test.js delete mode 100644 tests/versioned/apollo-server-koa/metrics.test.js delete mode 100644 tests/versioned/apollo-server-koa/newrelic.js delete mode 100644 tests/versioned/apollo-server-koa/package.json delete mode 100644 tests/versioned/apollo-server-koa/query-obfuscation.test.js delete mode 100644 tests/versioned/apollo-server-koa/segments.test.js delete mode 100644 tests/versioned/apollo-server-koa/transaction-naming.test.js delete mode 100644 tests/versioned/apollo-server-lambda/apollo-server-lambda-setup.js delete mode 100644 tests/versioned/apollo-server-lambda/attributes-tests.js delete mode 100644 tests/versioned/apollo-server-lambda/errors-tests.js delete mode 100644 tests/versioned/apollo-server-lambda/lambda-test-utils.js delete mode 100644 tests/versioned/apollo-server-lambda/newrelic.js delete mode 100644 tests/versioned/apollo-server-lambda/package.json delete mode 100644 tests/versioned/apollo-server-lambda/query-obfuscation-tests.js delete mode 100644 tests/versioned/apollo-server-lambda/segments.test.js delete mode 100644 tests/versioned/apollo-server-lambda/transaction-naming-tests.js diff --git a/tests/versioned/apollo-server-express/package.json b/tests/versioned/apollo-server-express/package.json index f0bfaed..5b87c88 100644 --- a/tests/versioned/apollo-server-express/package.json +++ b/tests/versioned/apollo-server-express/package.json @@ -12,7 +12,10 @@ "node": ">=16" }, "dependencies": { - "apollo-server-express": ">=2.14.0", + "apollo-server-express": { + "versions": ">=3.0.0", + "samples": 3 + }, "express": "4.17.1", "graphql": "15.8.0" }, diff --git a/tests/versioned/apollo-server-fastify/apollo-server-fastify-setup.js b/tests/versioned/apollo-server-fastify/apollo-server-fastify-setup.js deleted file mode 100644 index 2ba3b27..0000000 --- a/tests/versioned/apollo-server-fastify/apollo-server-fastify-setup.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') - -const utils = require('@newrelic/test-utilities') -utils.assert.extendTap(tap) - -const { getTypeDefs, resolvers } = require('../../data-definitions') -const setupErrorSchema = require('../error-setup') -const { clearCachedModules } = require('../../utils') - -const WEB_FRAMEWORK = 'WebFrameworkUri/Fastify' - -function setupApolloServerFastifyTests({ suiteName, createTests, pluginConfig }, config) { - tap.test(`apollo-server-fastify: ${suiteName}`, (t) => { - t.autoend() - - let server = null - let app = null - let serverUrl = null - let helper = null - - t.beforeEach(async () => { - // load default instrumentation - helper = utils.TestAgent.makeFullyInstrumented(config) - const createPlugin = require('../../../lib/create-plugin') - const nrApi = helper.getAgentApi() - - app = require('fastify')() - - // TODO: eventually use proper function for instrumenting and not .shim - const plugin = createPlugin(nrApi, pluginConfig) - - // Do after instrumentation to ensure hapi isn't loaded too soon. - const fastifyServerPkg = require('apollo-server-fastify') - const { ApolloServer, gql } = fastifyServerPkg - const schema = getTypeDefs(gql) - const errorSchema = setupErrorSchema(fastifyServerPkg, resolvers) - server = new ApolloServer({ - typeDefs: [schema, errorSchema], - resolvers, - plugins: [plugin] - }) - - await server.start() - app.register(server.createHandler()) - - return new Promise((resolve, reject) => { - app.listen(0, (err, address) => { - if (err) { - reject(err) - } - serverUrl = `${address}${server.graphqlPath}` - - t.context.helper = helper - t.context.serverUrl = serverUrl - resolve() - }) - }) - }) - - t.afterEach(() => { - server && server.stop() - app && app.close() - - helper.unload() - server = null - app = null - serverUrl = null - helper = null - - clearCachedModules(['fastify', 'apollo-server-fastify'], __dirname) - }) - - createTests(t, WEB_FRAMEWORK) - }) -} - -module.exports = { - setupApolloServerFastifyTests -} diff --git a/tests/versioned/apollo-server-fastify/attributes.test.js b/tests/versioned/apollo-server-fastify/attributes.test.js deleted file mode 100644 index 5546e72..0000000 --- a/tests/versioned/apollo-server-fastify/attributes.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const attributesTests = require('../attributes-tests') - -setupApolloServerFastifyTests(attributesTests) diff --git a/tests/versioned/apollo-server-fastify/errors.test.js b/tests/versioned/apollo-server-fastify/errors.test.js deleted file mode 100644 index 008e015..0000000 --- a/tests/versioned/apollo-server-fastify/errors.test.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const errorsTests = require('../errors-tests') - -setupApolloServerFastifyTests(errorsTests, { - distributed_tracing: { enabled: true } // enable span testing -}) diff --git a/tests/versioned/apollo-server-fastify/metrics.test.js b/tests/versioned/apollo-server-fastify/metrics.test.js deleted file mode 100644 index ab8ae67..0000000 --- a/tests/versioned/apollo-server-fastify/metrics.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const metricsTests = require('../../metrics-tests') - -setupApolloServerFastifyTests({ - suiteName: 'metrics', - createTests: metricsTests.bind(null, false) -}) - -setupApolloServerFastifyTests({ - suiteName: 'capture field metrics', - createTests: metricsTests.bind(null, true), - pluginConfig: { captureFieldMetrics: true } -}) diff --git a/tests/versioned/apollo-server-fastify/newrelic.js b/tests/versioned/apollo-server-fastify/newrelic.js deleted file mode 100644 index 5bfe537..0000000 --- a/tests/versioned/apollo-server-fastify/newrelic.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -exports.config = { - app_name: ['My Application'], - license_key: 'license key here', - logging: { - level: 'trace', - filepath: '../../../newrelic_agent.log' - }, - utilization: { - detect_aws: false, - detect_pcf: false, - detect_azure: false, - detect_gcp: false, - detect_docker: false - }, - transaction_tracer: { - enabled: true - } -} diff --git a/tests/versioned/apollo-server-fastify/package.json b/tests/versioned/apollo-server-fastify/package.json deleted file mode 100644 index 430f47e..0000000 --- a/tests/versioned/apollo-server-fastify/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "apollo-server-tests", - "targets": [{"name":"apollo-server-fastify","minAgentVersion":"@newrelic/apollo-server-plugin@1.0.0"}], - "version": "0.0.0", - "private": true, - "tests": [ - { - "engines": { - "node": ">=16" - }, - "dependencies": { - "apollo-server-fastify": ">=2.14.0", - "fastify": "3.17.0", - "graphql": "15.8.0" - }, - "files": [ - "segments.test.js", - "transaction-naming.test.js", - "errors.test.js", - "attributes.test.js", - "query-obfuscation.test.js", - "metrics.test.js" - ] - } - ], - "dependencies": {} -} diff --git a/tests/versioned/apollo-server-fastify/query-obfuscation.test.js b/tests/versioned/apollo-server-fastify/query-obfuscation.test.js deleted file mode 100644 index 844ee6a..0000000 --- a/tests/versioned/apollo-server-fastify/query-obfuscation.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const queryObfuscationTests = require('../query-obfuscation-tests') - -setupApolloServerFastifyTests(queryObfuscationTests) diff --git a/tests/versioned/apollo-server-fastify/segments.test.js b/tests/versioned/apollo-server-fastify/segments.test.js deleted file mode 100644 index 5eaf343..0000000 --- a/tests/versioned/apollo-server-fastify/segments.test.js +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { executeQuery, executeQueryBatch } = require('../../test-client') - -const ANON_PLACEHOLDER = '' -const UNKNOWN_OPERATION = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -// Varies based on apollo-server-fastify version. -// Nodejs/Middleware/Fastify///graphql OR -// Nodejs/Middleware/Fastify/handler//graphql -const GQL_MIDDLEWARE_SEGMENT_PATTERN = '//graphql' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const { checkResult } = require('../common') - -setupApolloServerFastifyTests({ - suiteName: 'fastify segments', - createTests: createFastifySegmentsTests, - pluginConfig: { - captureScalars: true - } -}) - -function createFastifySegmentsTests(t, frameworkName) { - const TRANSACTION_PREFIX = `WebTransaction/${frameworkName}/POST` - - t.test('anonymous query, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, single level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - libraries { - books { - author { - name - } - } - } - }` - - const path = 'libraries.books.author.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, multi-level should return deepest unique path', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.title` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous mutation, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `mutation { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named mutation, single level, should use mutation name', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${expectedName}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, with params', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - paramQuery(blah: "blah", blee: "blee") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "blah") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const path = 'library.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query with fragment, query first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - } - fragment LibraryBook on Book { - title - author { - name - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('named query with fragment, fragment first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `fragment LibraryBook on Book { - title - author { - name - } - } - query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('batch query should include segments for nested queries', (t) => { - const { helper, serverUrl } = t.context - - const expectedName1 = 'GetBookForLibrary' - const query1 = `query ${expectedName1} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const query2 = `mutation { - addThing(name: "added thing!") - }` - - const path = 'library.books' - - const queries = [query1, query2] - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart1 = `query/${expectedName1}/${path}` - const expectedQuery1Name = `${operationPart1}` - const operationPart2 = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedQuery2Name = `${operationPart2}` - - const batchTransactionPrefix = `${TRANSACTION_PREFIX}//batch` - - const expectedSegments = [ - { - name: `${batchTransactionPrefix}/${expectedQuery1Name}/${expectedQuery2Name}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart1}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - }, - { - name: `${OPERATION_PREFIX}/${operationPart2}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryBatch(serverUrl, queries, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.equal(result.length, 2) - - t.end() - }) - }) - }) - - // there will be no document/AST nor resolved operation - t.test('when the query cannot be parsed, should have operation placeholder', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//*`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${UNKNOWN_OPERATION}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_PARSE_FAILED') - - t.end() - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('when cannot validate, should include operation segment', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: GQL_MIDDLEWARE_SEGMENT_PATTERN, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_VALIDATION_FAILED') - - t.end() - }) - }) -} diff --git a/tests/versioned/apollo-server-fastify/transaction-naming.test.js b/tests/versioned/apollo-server-fastify/transaction-naming.test.js deleted file mode 100644 index 6254bd4..0000000 --- a/tests/versioned/apollo-server-fastify/transaction-naming.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerFastifyTests } = require('./apollo-server-fastify-setup') -const transactionNamingTests = require('../transaction-naming-tests') - -setupApolloServerFastifyTests(transactionNamingTests) diff --git a/tests/versioned/apollo-server-hapi/apollo-server-hapi-setup.js b/tests/versioned/apollo-server-hapi/apollo-server-hapi-setup.js deleted file mode 100644 index 07e65ca..0000000 --- a/tests/versioned/apollo-server-hapi/apollo-server-hapi-setup.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') - -const utils = require('@newrelic/test-utilities') -utils.assert.extendTap(tap) - -const { getTypeDefs, resolvers } = require('../../data-definitions') -const setupErrorSchema = require('../error-setup') -const { clearCachedModules } = require('../../utils') - -const WEB_FRAMEWORK = 'Hapi' - -function setupApolloServerHapiTests({ suiteName, createTests, pluginConfig }, config) { - tap.test(`apollo-server-hapi: ${suiteName}`, (t) => { - t.autoend() - - let server = null - let hapiServer = null - let serverUrl = null - let helper = null - - t.beforeEach(async () => { - // load default instrumentation. hapi being critical - helper = utils.TestAgent.makeFullyInstrumented(config) - const createPlugin = require('../../../lib/create-plugin') - const nrApi = helper.getAgentApi() - - // TODO: eventually use proper function for instrumenting and not .shim - const plugin = createPlugin(nrApi, pluginConfig) - - const Hapi = require('@hapi/hapi') - - const graphqlPath = '/gql' - - // Do after instrumentation to ensure hapi isn't loaded too soon. - const hapiServerPkg = require('apollo-server-hapi') - const { ApolloServer, gql } = hapiServerPkg - const schema = getTypeDefs(gql) - const errorSchema = setupErrorSchema(hapiServerPkg, resolvers) - server = new ApolloServer({ - typeDefs: [schema, errorSchema], - resolvers, - plugins: [plugin] - }) - - hapiServer = Hapi.server({ - host: 'localhost' - }) - - await server.start() - await server.applyMiddleware({ app: hapiServer, path: graphqlPath }) - - await hapiServer.start() - - serverUrl = `http://localhost:${hapiServer.info.port}${graphqlPath}` - t.context.helper = helper - t.context.serverUrl = serverUrl - }) - - t.afterEach(() => { - hapiServer && hapiServer.stop() - server && server.stop() - - helper.unload() - server = null - serverUrl = null - helper = null - - clearCachedModules(['@hapi/hapi', 'apollo-server-hapi'], __dirname) - }) - - createTests(t, WEB_FRAMEWORK) - }) -} - -module.exports = { - setupApolloServerHapiTests -} diff --git a/tests/versioned/apollo-server-hapi/attributes.test.js b/tests/versioned/apollo-server-hapi/attributes.test.js deleted file mode 100644 index e22a2ab..0000000 --- a/tests/versioned/apollo-server-hapi/attributes.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const attributesTests = require('../attributes-tests') - -setupApolloServerHapiTests(attributesTests) diff --git a/tests/versioned/apollo-server-hapi/errors.test.js b/tests/versioned/apollo-server-hapi/errors.test.js deleted file mode 100644 index b320269..0000000 --- a/tests/versioned/apollo-server-hapi/errors.test.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const errorsTests = require('../errors-tests') - -setupApolloServerHapiTests(errorsTests, { - distributed_tracing: { enabled: true } // enable span testing -}) diff --git a/tests/versioned/apollo-server-hapi/metrics.test.js b/tests/versioned/apollo-server-hapi/metrics.test.js deleted file mode 100644 index 187c017..0000000 --- a/tests/versioned/apollo-server-hapi/metrics.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const metricsTests = require('../../metrics-tests') - -setupApolloServerHapiTests({ - suiteName: 'metrics', - createTests: metricsTests.bind(null, false) -}) - -setupApolloServerHapiTests({ - suiteName: 'capture field metrics', - createTests: metricsTests.bind(null, true), - pluginConfig: { captureFieldMetrics: true } -}) diff --git a/tests/versioned/apollo-server-hapi/newrelic.js b/tests/versioned/apollo-server-hapi/newrelic.js deleted file mode 100644 index 5bfe537..0000000 --- a/tests/versioned/apollo-server-hapi/newrelic.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -exports.config = { - app_name: ['My Application'], - license_key: 'license key here', - logging: { - level: 'trace', - filepath: '../../../newrelic_agent.log' - }, - utilization: { - detect_aws: false, - detect_pcf: false, - detect_azure: false, - detect_gcp: false, - detect_docker: false - }, - transaction_tracer: { - enabled: true - } -} diff --git a/tests/versioned/apollo-server-hapi/package.json b/tests/versioned/apollo-server-hapi/package.json deleted file mode 100644 index 6d9920a..0000000 --- a/tests/versioned/apollo-server-hapi/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "apollo-server-tests", - "targets": [{"name":"apollo-server-hapi","minAgentVersion":"@newrelic/apollo-server-plugin@1.0.0"}], - "version": "0.0.0", - "private": true, - "tests": [ - { - "engines": { - "node": ">=16" - }, - "dependencies": { - "apollo-server-hapi": ">=3.0.0", - "@hapi/hapi": "^20.1.2" - }, - "files": [ - "segments.test.js", - "transaction-naming.test.js", - "errors.test.js", - "attributes.test.js", - "query-obfuscation.test.js", - "metrics.test.js" - ] - } - ], - "dependencies": {} -} diff --git a/tests/versioned/apollo-server-hapi/query-obfuscation.test.js b/tests/versioned/apollo-server-hapi/query-obfuscation.test.js deleted file mode 100644 index cfcb8bc..0000000 --- a/tests/versioned/apollo-server-hapi/query-obfuscation.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const queryObfuscationTests = require('../query-obfuscation-tests') - -setupApolloServerHapiTests(queryObfuscationTests) diff --git a/tests/versioned/apollo-server-hapi/segments.test.js b/tests/versioned/apollo-server-hapi/segments.test.js deleted file mode 100644 index 160e573..0000000 --- a/tests/versioned/apollo-server-hapi/segments.test.js +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { executeQuery, executeQueryBatch } = require('../../test-client') - -const ANON_PLACEHOLDER = '' -const UNKNOWN_OPERATION = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const { checkResult } = require('../common') - -setupApolloServerHapiTests({ - suiteName: 'hapi segments', - createTests: createHapiSegmentsTests, - pluginConfig: { - captureScalars: true - } -}) - -function createHapiSegmentsTests(t, frameworkName) { - const TRANSACTION_PREFIX = `WebTransaction/${frameworkName}/POST` - - t.test('anonymous query, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, single level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - libraries { - books { - author { - name - } - } - } - }` - - const path = 'libraries.books.author.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, multi-level should return deepest unique path', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.title` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous mutation, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `mutation { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named mutation, single level, should use mutation name', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${expectedName}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, with params', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - paramQuery(blah: "blah", blee: "blee") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "blah") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const path = 'library.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query with fragment, query first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - } - fragment LibraryBook on Book { - title - author { - name - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('named query with fragment, fragment first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `fragment LibraryBook on Book { - title - author { - name - } - } - query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('batch query should include segments for nested queries', (t) => { - const { helper, serverUrl } = t.context - - const expectedName1 = 'GetBookForLibrary' - const query1 = `query ${expectedName1} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const query2 = `mutation { - addThing(name: "added thing!") - }` - - const path1 = 'library.books' - - const queries = [query1, query2] - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart1 = `query/${expectedName1}/${path1}` - const expectedQuery1Name = `${operationPart1}` - const operationPart2 = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedQuery2Name = `${operationPart2}` - - const batchTransactionPrefix = `${TRANSACTION_PREFIX}//batch` - - const expectedSegments = [ - { - name: `${batchTransactionPrefix}/${expectedQuery1Name}/${expectedQuery2Name}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart1}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - }, - { - name: `${OPERATION_PREFIX}/${operationPart2}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryBatch(serverUrl, queries, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.equal(result.length, 2) - - t.end() - }) - }) - }) - - // there will be no document/AST nor resolved operation - t.test('when the query cannot be parsed, should have operation placeholder', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//*`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${UNKNOWN_OPERATION}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_PARSE_FAILED') - - t.end() - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('when cannot validate, should include operation segment', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Hapi/handler//gql', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_VALIDATION_FAILED') - - t.end() - }) - }) -} diff --git a/tests/versioned/apollo-server-hapi/transaction-naming.test.js b/tests/versioned/apollo-server-hapi/transaction-naming.test.js deleted file mode 100644 index 9f7f70c..0000000 --- a/tests/versioned/apollo-server-hapi/transaction-naming.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerHapiTests } = require('./apollo-server-hapi-setup') -const transactionNamingTests = require('../transaction-naming-tests') - -setupApolloServerHapiTests(transactionNamingTests) diff --git a/tests/versioned/apollo-server-koa/apollo-server-koa-setup.js b/tests/versioned/apollo-server-koa/apollo-server-koa-setup.js deleted file mode 100644 index 1e29122..0000000 --- a/tests/versioned/apollo-server-koa/apollo-server-koa-setup.js +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') - -const utils = require('@newrelic/test-utilities') -utils.assert.extendTap(tap) - -const { getTypeDefs, resolvers } = require('../../data-definitions') -const setupErrorSchema = require('../error-setup') -const { clearCachedModules } = require('../../utils') - -const WEB_FRAMEWORK = 'WebFrameworkUri/Koa' - -function setupApolloServerKoaTests({ suiteName, createTests, pluginConfig }, config) { - tap.test(`apollo-server-koa: ${suiteName}`, (t) => { - t.autoend() - - let server = null - let koaServer = null - let app = null - let serverUrl = null - let helper = null - - t.beforeEach(async () => { - // load default instrumentation - helper = utils.TestAgent.makeFullyInstrumented(config) - const createPlugin = require('../../../lib/create-plugin') - const nrApi = helper.getAgentApi() - - const Koa = require('koa') - - app = new Koa() - - // TODO: eventually use proper function for instrumenting and not .shim - const plugin = createPlugin(nrApi, pluginConfig) - - const graphqlPath = '/gql' - - // Do after instrumentation to ensure hapi isn't loaded too soon. - const koaServerPkg = require('apollo-server-koa') - const { ApolloServer, gql } = koaServerPkg - const schema = getTypeDefs(gql) - const errorSchema = setupErrorSchema(koaServerPkg, resolvers) - server = new ApolloServer({ - typeDefs: [schema, errorSchema], - resolvers, - plugins: [plugin] - }) - - await server.start() - server.applyMiddleware({ app, path: graphqlPath }) - - return new Promise((resolve, reject) => { - koaServer = app.listen(0, (err) => { - if (err) { - reject(err) - } - serverUrl = `http://localhost:${koaServer.address().port}${server.graphqlPath}` - - t.context.helper = helper - t.context.serverUrl = serverUrl - resolve() - }) - }) - }) - - t.afterEach(() => { - server && server.stop() - koaServer && koaServer.close() - - helper.unload() - server = null - app = null - serverUrl = null - helper = null - - clearCachedModules(['koa', 'apollo-server-koa'], __dirname) - }) - - createTests(t, WEB_FRAMEWORK) - }) -} - -module.exports = { - setupApolloServerKoaTests -} diff --git a/tests/versioned/apollo-server-koa/attributes.test.js b/tests/versioned/apollo-server-koa/attributes.test.js deleted file mode 100644 index c2234f1..0000000 --- a/tests/versioned/apollo-server-koa/attributes.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const attributesTests = require('../attributes-tests') - -setupApolloServerKoaTests(attributesTests) diff --git a/tests/versioned/apollo-server-koa/errors.test.js b/tests/versioned/apollo-server-koa/errors.test.js deleted file mode 100644 index bec6202..0000000 --- a/tests/versioned/apollo-server-koa/errors.test.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const errorsTests = require('../errors-tests') - -setupApolloServerKoaTests(errorsTests, { - distributed_tracing: { enabled: true } // enable span testing -}) diff --git a/tests/versioned/apollo-server-koa/metrics.test.js b/tests/versioned/apollo-server-koa/metrics.test.js deleted file mode 100644 index f352ce6..0000000 --- a/tests/versioned/apollo-server-koa/metrics.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const metricsTests = require('../../metrics-tests') - -setupApolloServerKoaTests({ - suiteName: 'metrics', - createTests: metricsTests.bind(null, false) -}) - -setupApolloServerKoaTests({ - suiteName: 'capture field metrics', - createTests: metricsTests.bind(null, true), - pluginConfig: { captureFieldMetrics: true } -}) diff --git a/tests/versioned/apollo-server-koa/newrelic.js b/tests/versioned/apollo-server-koa/newrelic.js deleted file mode 100644 index 5bfe537..0000000 --- a/tests/versioned/apollo-server-koa/newrelic.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -exports.config = { - app_name: ['My Application'], - license_key: 'license key here', - logging: { - level: 'trace', - filepath: '../../../newrelic_agent.log' - }, - utilization: { - detect_aws: false, - detect_pcf: false, - detect_azure: false, - detect_gcp: false, - detect_docker: false - }, - transaction_tracer: { - enabled: true - } -} diff --git a/tests/versioned/apollo-server-koa/package.json b/tests/versioned/apollo-server-koa/package.json deleted file mode 100644 index 77dcb99..0000000 --- a/tests/versioned/apollo-server-koa/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "apollo-server-tests", - "version": "0.0.0", - "targets": [{"name":"apollo-server-koa","minAgentVersion":"@newrelic/apollo-server-plugin@1.0.0"}], - "private": true, - "tests": [ - { - "engines": { - "node": ">=16" - }, - "dependencies": { - "apollo-server-koa": ">=2.14.0 <3.0.0", - "graphql": "15.8.0" - }, - "files": [ - "segments.test.js", - "transaction-naming.test.js", - "errors.test.js", - "attributes.test.js", - "query-obfuscation.test.js", - "metrics.test.js" - ] - }, - { - "engines": { - "node": ">=16" - }, - "dependencies": { - "apollo-server-koa": ">=3.4.0", - "koa": "^2.13.1" - }, - "files": [ - "segments.test.js", - "transaction-naming.test.js", - "errors.test.js", - "attributes.test.js", - "query-obfuscation.test.js", - "metrics.test.js" - ] - } - ], - "dependencies": {} -} diff --git a/tests/versioned/apollo-server-koa/query-obfuscation.test.js b/tests/versioned/apollo-server-koa/query-obfuscation.test.js deleted file mode 100644 index 250136d..0000000 --- a/tests/versioned/apollo-server-koa/query-obfuscation.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const queryObfuscationTests = require('../query-obfuscation-tests') - -setupApolloServerKoaTests(queryObfuscationTests) diff --git a/tests/versioned/apollo-server-koa/segments.test.js b/tests/versioned/apollo-server-koa/segments.test.js deleted file mode 100644 index 50b644d..0000000 --- a/tests/versioned/apollo-server-koa/segments.test.js +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { executeQuery, executeQueryBatch } = require('../../test-client') - -const ANON_PLACEHOLDER = '' -const UNKNOWN_OPERATION = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const { checkResult } = require('../common') - -setupApolloServerKoaTests({ - suiteName: 'koa segments', - createTests: createKoaSegmentsTests, - pluginConfig: { - captureScalars: true - } -}) - -function createKoaSegmentsTests(t, frameworkName) { - const TRANSACTION_PREFIX = `WebTransaction/${frameworkName}/POST` - - t.test('anonymous query, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, single level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - libraries { - books { - author { - name - } - } - } - }` - - const path = 'libraries.books.author.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, multi-level should return deepest unique path', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.title` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous mutation, single level', (t) => { - const { helper, serverUrl } = t.context - - const query = `mutation { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named mutation, single level, should use mutation name', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${expectedName}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('anonymous query, with params', (t) => { - const { helper, serverUrl } = t.context - - const query = `query { - paramQuery(blah: "blah", blee: "blee") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "blah") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query, with params, multi-level', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const path = 'library.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.end() - }) - }) - }) - - t.test('named query with fragment, query first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - } - fragment LibraryBook on Book { - title - author { - name - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('named query with fragment, fragment first', (t) => { - const { helper, serverUrl } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `fragment LibraryBook on Book { - title - author { - name - } - } - query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, query, (err) => { - t.error(err) - t.end() - }) - }) - - t.test('batch query should include segments for nested queries', (t) => { - const { helper, serverUrl } = t.context - - const expectedName1 = 'GetBookForLibrary' - const query1 = `query ${expectedName1} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const query2 = `mutation { - addThing(name: "added thing!") - }` - - const path1 = 'library.books' - - const queries = [query1, query2] - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart1 = `query/${expectedName1}/${path1}` - const expectedQuery1Name = `${operationPart1}` - const operationPart2 = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedQuery2Name = `${operationPart2}` - - const batchTransactionPrefix = `${TRANSACTION_PREFIX}//batch` - - const expectedSegments = [ - { - name: `${batchTransactionPrefix}/${expectedQuery1Name}/${expectedQuery2Name}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart1}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - }, - { - name: `${OPERATION_PREFIX}/${operationPart2}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryBatch(serverUrl, queries, (err, result) => { - t.error(err) - checkResult(t, result, () => { - t.equal(result.length, 2) - - t.end() - }) - }) - }) - - // there will be no document/AST nor resolved operation - t.test('when the query cannot be parsed, should have operation placeholder', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//*`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${UNKNOWN_OPERATION}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_PARSE_FAILED') - - t.end() - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('when cannot validate, should include operation segment', (t) => { - const { helper, serverUrl } = t.context - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: 'Nodejs/Middleware/Koa', - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQuery(serverUrl, invalidQuery, (err, result) => { - t.error(err) - - t.ok(result) - t.ok(result.errors) - t.equal(result.errors.length, 1) // should have one parsing error - - const [parseError] = result.errors - t.equal(parseError.extensions.code, 'GRAPHQL_VALIDATION_FAILED') - - t.end() - }) - }) -} diff --git a/tests/versioned/apollo-server-koa/transaction-naming.test.js b/tests/versioned/apollo-server-koa/transaction-naming.test.js deleted file mode 100644 index 47b3122..0000000 --- a/tests/versioned/apollo-server-koa/transaction-naming.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { setupApolloServerKoaTests } = require('./apollo-server-koa-setup') -const transactionNamingTests = require('../transaction-naming-tests') - -setupApolloServerKoaTests(transactionNamingTests) diff --git a/tests/versioned/apollo-server-lambda/apollo-server-lambda-setup.js b/tests/versioned/apollo-server-lambda/apollo-server-lambda-setup.js deleted file mode 100644 index c7b2a77..0000000 --- a/tests/versioned/apollo-server-lambda/apollo-server-lambda-setup.js +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') -const os = require('os') - -const agentTesting = require('../../agent-testing') -const utils = require('@newrelic/test-utilities') -utils.assert.extendTap(tap) - -const { getTypeDefs, resolvers } = require('../../data-definitions') -const setupErrorSchema = require('../error-setup') -const { clearCachedModules } = require('../../utils') - -const WEB_FRAMEWORK = 'WebFrameworkUri' - -function setupApolloServerLambdaTests({ suiteName, createTests, pluginConfig }, config) { - tap.test(`apollo-server-lambda: ${suiteName}`, (t) => { - t.autoend() - - let server = null - let helper = null - let handler = null - let patchedHandler = null - let stubContext = null - - /** - * Account ID is required for serverless in order to enable - * distributed tracing. - */ - agentTesting.temporarySetEnv(t, 'NEW_RELIC_ACCOUNT_ID', 'eeeeee') - agentTesting.temporarySetEnv(t, 'NEWRELIC_PIPE_PATH', os.devNull) - - t.beforeEach((t) => { - const lambdaServerPkg = require('apollo-server-lambda') - const { ApolloServer, gql } = lambdaServerPkg - const schema = getTypeDefs(gql) - const errorSchema = setupErrorSchema(lambdaServerPkg, resolvers) - const { version } = require('apollo-server-lambda/package') - - helper = utils.TestAgent.makeFullyInstrumented(config) - - const createPlugin = require('../../../lib/create-plugin') - const nrApi = helper.getAgentApi() - - // TODO: eventually use proper function for instrumenting and not .shim - const plugin = createPlugin(nrApi, pluginConfig) - - ;(stubContext = { - done: () => {}, - succeed: () => {}, - fail: () => {}, - functionName: 'functionName', - functionVersion: 'TestVersion', - invokedFunctionArn: 'arn:test:function', - memoryLimitInMB: '128', - awsRequestId: 'testid' - }), - (server = new ApolloServer({ - typeDefs: [schema, errorSchema], - resolvers, - plugins: [plugin], - context: ({ event, context }) => ({ - headers: event.headers, - functionName: context.functionName, - event, - context - }) - })) - - handler = server.createHandler() - - patchedHandler = nrApi.setLambdaHandler(handler) - - t.context.modVersion = version - t.context.helper = helper - t.context.patchedHandler = patchedHandler - t.context.stubContext = stubContext - }) - - t.afterEach(() => { - server && server.stop() - - helper.unload() - server = null - helper = null - handler = null - patchedHandler = null - stubContext = null - - clearCachedModules(['apollo-server-lambda'], __dirname) - }) - - createTests(t, WEB_FRAMEWORK) - }) -} - -module.exports = { - setupApolloServerLambdaTests -} diff --git a/tests/versioned/apollo-server-lambda/attributes-tests.js b/tests/versioned/apollo-server-lambda/attributes-tests.js deleted file mode 100644 index f225ea2..0000000 --- a/tests/versioned/apollo-server-lambda/attributes-tests.js +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { - executeQueryJson, - executeQueryAssertResult, - createApiEvent -} = require('./lambda-test-utils') -const { findSegmentByName } = require('../../agent-testing') -const { checkResult } = require('../common') - -const SEGMENT_DESTINATION = 0x20 - -const ANON_PLACEHOLDER = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' - -const { setupApolloServerLambdaTests } = require('./apollo-server-lambda-setup') - -setupApolloServerLambdaTests({ - suiteName: 'lambda attributes', - createTests: createAttributesTests, - pluginConfig: { - captureScalars: true - } -}) - -function createAttributesTests(t) { - t.test('anon query should capture standard attributes except operation name', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${ANON_PLACEHOLDER}/hello` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.type': 'query' - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const hasAttribute = Object.hasOwnProperty.bind(operationAttributes) - t.notOk(hasAttribute('graphql.operation.name')) - - const resolveHelloSegment = operationSegment.children[0] - - const expectedResolveAttributes = { - 'graphql.field.name': 'hello', - 'graphql.field.returnType': 'String', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'hello' - } - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedResolveAttributes, 'should have field resolve attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query should capture all standard attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/hello` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.name': expectedName - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const resolveHelloSegment = operationSegment.children[0] - - const expectedResolveAttributes = { - 'graphql.field.name': 'hello', - 'graphql.field.returnType': 'String', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'hello' - } - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedResolveAttributes, 'should have field resolve attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, multi-level, should capture deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/${path}` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.name': expectedName - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const [resolveLibrariesSegment, resolveBooksSegment] = operationSegment.children - - const expectedLibrariesAttributes = { - 'graphql.field.name': 'libraries', - 'graphql.field.returnType': '[Library]', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'libraries' - } - - const resolveLibrariesAttributes = resolveLibrariesSegment.attributes.get(SEGMENT_DESTINATION) - t.match( - resolveLibrariesAttributes, - expectedLibrariesAttributes, - 'should have field resolve attributes for libraries' - ) - - const expectedBooksAttributes = { - 'graphql.field.name': 'books', - 'graphql.field.returnType': '[Book!]', - 'graphql.field.parentType': 'Library', - 'graphql.field.path': 'libraries.book' - } - - const resolveBooksAttributes = resolveBooksSegment.attributes.get(SEGMENT_DESTINATION) - t.match( - resolveBooksAttributes, - expectedBooksAttributes, - 'should have field resolve attributes for books' - ) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('query with alias should use alias in path attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - alias: libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/${path}` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.name': expectedName - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const [resolveLibrariesSegment, resolveBooksSegment] = operationSegment.children - - const expectedLibrariesAttributes = { - 'graphql.field.name': 'libraries', - 'graphql.field.returnType': '[Library]', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'alias' - } - - const resolveLibrariesAttributes = resolveLibrariesSegment.attributes.get(SEGMENT_DESTINATION) - t.match( - resolveLibrariesAttributes, - expectedLibrariesAttributes, - 'should have field resolve attributes for libraries' - ) - - const expectedBooksAttributes = { - 'graphql.field.name': 'books', - 'graphql.field.returnType': '[Book!]', - 'graphql.field.parentType': 'Library', - 'graphql.field.path': 'alias.book' - } - - const resolveBooksAttributes = resolveBooksSegment.attributes.get(SEGMENT_DESTINATION) - t.match( - resolveBooksAttributes, - expectedBooksAttributes, - 'should have field resolve attributes for books' - ) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named mutation should capture all standard attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/mutation/${expectedName}/addThing` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.type': 'mutation', - 'graphql.operation.name': expectedName - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const resolveHelloSegment = operationSegment.children[0] - - const expectedResolveAttributes = { - 'graphql.field.name': 'addThing', - 'graphql.field.returnType': 'String', - 'graphql.field.parentType': 'Mutation', - 'graphql.field.path': 'addThing' - } - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedResolveAttributes, 'should have field resolve attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named mutation should not capture args by default', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/mutation/${expectedName}/addThing` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const resolveHelloSegment = operationSegment.children[0] - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - - const hasAttribute = Object.hasOwnProperty.bind(resolveAttributes) - t.notOk(hasAttribute('graphql.field.args.name')) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named mutation should capture args when added to include list', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - helper.agent.config.attributes.include = ['graphql.field.args.*'] - helper.agent.config.emit('attributes.include') - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/mutation/${expectedName}/addThing` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const resolveHelloSegment = operationSegment.children[0] - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, { 'graphql.field.args.name': 'added thing!' }) - t.match(operationAttributes, { 'graphql.field.args.name': 'added thing!' }) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query should capture args when added to include list', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - helper.agent.config.attributes.include = ['graphql.field.args.*'] - helper.agent.config.emit('attributes.include') - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "first", blee: "second") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/paramQuery` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const resolveHelloSegment = operationSegment.children[0] - - const expectedArgAttributes = { - 'graphql.field.args.blah': 'first', - 'graphql.field.args.blee': 'second' - } - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedArgAttributes) - t.match(operationAttributes, expectedArgAttributes) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('query with variables should capture args when added to include list', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - helper.agent.config.attributes.include = ['graphql.field.args.*'] - helper.agent.config.emit('attributes.include') - - const expectedName = 'ParamQueryWithArgs' - const query = `query ${expectedName}($arg1: String!, $arg2: String) { - paramQuery(blah: $arg1, blee: $arg2) - }` - - const queryJson = { - operationName: expectedName, - query: query, - variables: { - arg1: 'first', - arg2: 'second' - } - } - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/paramQuery` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const resolveHelloSegment = operationSegment.children[0] - - const expectedArgAttributes = { - 'graphql.field.args.blah': 'first', - 'graphql.field.args.blee': 'second' - } - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedArgAttributes) - t.match(operationAttributes, expectedArgAttributes) - }) - - executeQueryJson({ - handler: patchedHandler, - query: queryJson, - context: stubContext, - modVersion, - t - }) - }) - - t.test('should capture query in operation segment attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'Greetings' - const query = `query ${expectedName} { - ciao - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/ciao` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const expectedOperationAttributes = { - 'graphql.operation.query': query - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('union, should capture all expected attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResult' - const query = `query ${expectedName} { - search(contains: "Ollies") { - __typename - ... on Book { - title - } - } - }` - - const deepestPath = 'search.title' - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/${deepestPath}` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.name': expectedName, - 'graphql.operation.query': query.replace('contains: "Ollies"', '***') - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const resolveHelloSegment = operationSegment.children[0] - - const expectedResolveAttributes = { - 'graphql.field.name': 'search', - 'graphql.field.returnType': '[SearchResult!]', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'search' - } - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedResolveAttributes, 'should have field resolve attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('union, multiple inline fragments, should return expected attributes', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResult' - const query = `query ${expectedName} { - search(contains: "Node") { - __typename - ... on Magazine { - title - } - ... on Book { - title - } - } - }` - - const deepestPath = 'search' - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/${deepestPath}` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.name': expectedName, - 'graphql.operation.query': query.replace('contains: "Node"', '***') - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - - const resolveHelloSegment = operationSegment.children[0] - - const expectedResolveAttributes = { - 'graphql.field.name': 'search', - 'graphql.field.returnType': '[SearchResult!]', - 'graphql.field.parentType': 'Query', - 'graphql.field.path': 'search' - } - - const resolveAttributes = resolveHelloSegment.attributes.get(SEGMENT_DESTINATION) - t.match(resolveAttributes, expectedResolveAttributes, 'should have field resolve attributes') - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('should capture all attributes on multiple queries', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - let count = 0 - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/hello` - - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - if (!operationSegment) { - const err = new Error(`Cannot find operation segment with name ${operationName}`) - t.error(err) - return - } - - const expectedOperationAttributes = { - 'graphql.operation.type': 'query', - 'graphql.operation.query': query, - 'graphql.operation.name': expectedName - } - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - - t.match(operationAttributes, expectedOperationAttributes, 'should have operation attributes') - count++ - }) - - const jsonQuery = JSON.stringify({ query }) - const event = createApiEvent(jsonQuery) - - if (modVersion < '2.21.1') { - patchedHandler(event, stubContext, (err, result) => { - t.error(err) - checkResult(t, result, () => { - patchedHandler(event, stubContext, (err2, result2) => { - t.error(err2) - checkResult(t, result2, () => { - t.equal(count, 2, 'should have checked 2 transactions') - t.end() - }) - }) - }) - }) - } else { - patchedHandler(event, stubContext).then((result) => { - checkResult(t, result, async () => { - patchedHandler(event, stubContext).then((result2) => { - checkResult(t, result2, () => { - t.equal(count, 2, 'should have checked 2 transactions') - t.end() - }) - }) - }) - }) - } - }) -} diff --git a/tests/versioned/apollo-server-lambda/errors-tests.js b/tests/versioned/apollo-server-lambda/errors-tests.js deleted file mode 100644 index e8c24a1..0000000 --- a/tests/versioned/apollo-server-lambda/errors-tests.js +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { executeQueryAssertErrors } = require('./lambda-test-utils') -const agentTesting = require('../../agent-testing') - -const ANON_PLACEHOLDER = '' -const UNKNOWN_OPERATION = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -const { setupApolloServerLambdaTests } = require('./apollo-server-lambda-setup') - -function createErrorTests(t) { - t.test('parsing error should be noticed and assigned to operation span', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedErrorMessage = 'Syntax Error: Expected Name, found .' - const expectedErrorType = 'GraphQLError' - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - const errorTraces = agentTesting.getErrorTraces(helper.agent) - t.equal(errorTraces.length, 1) - - const errorTrace = errorTraces[0] - - const [, transactionName, errorMessage, errorType, params] = errorTrace - t.equal(transactionName, transaction.name) - t.equal(errorMessage, expectedErrorMessage) - t.equal(errorType, expectedErrorType) - - const { agentAttributes } = params - - t.ok(agentAttributes.spanId) - - const matchingSpan = agentTesting.findSpanById(helper.agent, agentAttributes.spanId) - - const { attributes, intrinsics } = matchingSpan - t.equal(intrinsics.name, `${OPERATION_PREFIX}/${UNKNOWN_OPERATION}`) - t.equal(attributes['error.message'], expectedErrorMessage) - t.equal(attributes['error.class'], expectedErrorType) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_PARSE_FAILED' - }) - }) - - t.test('validation error should be noticed and assigned to operation span', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedErrorMessage = 'Cannot query field "doesnotexist" on type "Book".' - const expectedErrorType = 'GraphQLError' - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const deepestPath = 'libraries.books.doesnotexist.name' - const expectedOperationName = `${OPERATION_PREFIX}/query/${ANON_PLACEHOLDER}/${deepestPath}` - - helper.agent.on('transactionFinished', (transaction) => { - const errorTraces = agentTesting.getErrorTraces(helper.agent) - t.equal(errorTraces.length, 1) - - const errorTrace = errorTraces[0] - - const [, transactionName, errorMessage, errorType, params] = errorTrace - t.equal(transactionName, transaction.name) - t.equal(errorMessage, expectedErrorMessage) - t.equal(errorType, expectedErrorType) - - const { agentAttributes } = params - - t.ok(agentAttributes.spanId) - - const matchingSpan = agentTesting.findSpanById(helper.agent, agentAttributes.spanId) - - const { attributes, intrinsics } = matchingSpan - t.equal(intrinsics.name, expectedOperationName) - t.equal(attributes['error.message'], expectedErrorMessage) - t.equal(attributes['error.class'], expectedErrorType) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_VALIDATION_FAILED' - }) - }) - - t.test('resolver error should be noticed and assigned to resolve span', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedErrorMessage = 'Boom goes the dynamite!' - const expectedErrorType = 'Error' - - const expectedName = 'BOOM' - const invalidQuery = `query ${expectedName} { - boom - }` - - const expectedResolveName = `${RESOLVE_PREFIX}/boom` - - helper.agent.on('transactionFinished', (transaction) => { - const errorTraces = agentTesting.getErrorTraces(helper.agent) - t.equal(errorTraces.length, 1) - - const errorTrace = errorTraces[0] - - const [, transactionName, errorMessage, errorType, params] = errorTrace - t.equal(transactionName, transaction.name) - t.equal(errorMessage, expectedErrorMessage) - t.equal(errorType, expectedErrorType) - - const { agentAttributes } = params - - t.ok(agentAttributes.spanId) - - const matchingSpan = agentTesting.findSpanById(helper.agent, agentAttributes.spanId) - - const { attributes, intrinsics } = matchingSpan - t.equal(intrinsics.name, expectedResolveName) - t.equal(attributes['error.message'], expectedErrorMessage) - t.equal(attributes['error.class'], expectedErrorType) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'INTERNAL_SERVER_ERROR' - }) - }) -} - -setupApolloServerLambdaTests({ - suiteName: 'lambda errors', - createTests: createErrorTests, - pluginConfig: { - captureScalars: true - } -}) diff --git a/tests/versioned/apollo-server-lambda/lambda-test-utils.js b/tests/versioned/apollo-server-lambda/lambda-test-utils.js deleted file mode 100644 index b716b6e..0000000 --- a/tests/versioned/apollo-server-lambda/lambda-test-utils.js +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' -const { checkResult } = require('../common') -const utils = module.exports - -/** - * Apollo Server Lambda introduced promise-based handlers - * in [2.21.1](https://github.com/apollographql/apollo-server/blob/release-3.0/CHANGELOG.md#v2211) - * and in 3.0.0 requires promise-based handlers, this helper - * checks and decides which method to execute lambda handler - */ -function requiresCallback(version) { - return version < '2.21.1' -} - -/** - * callback that verifies no error occurred - * and result is as expected - * - * @param {Tap.Test} t - * @param {Error} err - * @param {*} result of lambda handler - */ -function resultCallback(t, err, result) { - t.error(err) - checkResult(t, result, () => { - t.end() - }) -} - -/** - * Executes a lambda handler and asserts no errors occurred - * and result is as expected - * - * @param {Object} params - * @param {Function} params.handler to execute - * @param {string} params.query to execute - * @param {Object} params.context lambda context - * @param {string} params.modVersion version of lambda-server-lambda package - * @param {Tap.Test} params.t - */ -utils.executeQueryAssertResult = async function executeQueryAssertResult({ - handler, - query, - context, - modVersion, - t -}) { - const jsonQuery = JSON.stringify({ query }) - const event = utils.createApiEvent(jsonQuery) - - if (requiresCallback(modVersion)) { - handler(event, context, resultCallback.bind(null, t)) - } else { - const result = await handler(event, context) - checkResult(t, result, () => { - t.end() - }) - } -} - -/** - * Executes a lambda handler and asserts no errors occurred - * Only diff between this and executeQueryAssertResult is the query - * contains more than a query string - * - * @param {Object} params - * @param {Function} params.handler to execute - * @param {string} params.query to execute - * @param {Object} params.context lambda context - * @param {string} params.modVersion version of lambda-server-lambda package - * @param {Tap.Test} params.t - */ -utils.executeQueryJson = async function executeQueryJson({ - handler, - query, - context, - modVersion, - t -}) { - const jsonQuery = JSON.stringify(query) - const event = utils.createApiEvent(jsonQuery) - - if (requiresCallback(modVersion)) { - handler(event, context, (err) => { - t.error(err) - t.end() - }) - } else { - await handler(event, context) - t.end() - } -} - -/** - * Executes a lambda handler with a batch of queries - * and asserts no errors occurred and result is as - * expected - * - * @param {Object} params - * @param {Function} params.handler to execute - * @param {string} params.queries to execute - * @param {Object} params.context lambda context - * @param {string} params.modVersion version of lambda-server-lambda package - * @param {Tap.Test} params.t - */ -utils.executeBatchAssertResult = async function executeBatchAssertResult({ - handler, - queries, - context, - modVersion, - t -}) { - const data = queries.map((innerQuery) => { - return { query: innerQuery } - }) - const jsonQuery = JSON.stringify(data) - const event = utils.createApiEvent(jsonQuery) - - if (requiresCallback(modVersion)) { - handler(event, context, resultCallback.bind(null, t)) - } else { - const result = await handler(event, context) - t.ok(result.body) - - const jsonResult = JSON.parse(result.body) - t.equal(jsonResult.length, 2) - checkResult(t, result, () => { - t.end() - }) - } -} - -/** - * A series of assertions used to assert an error - * occurred when executing a lambda handler - * - * @param {*} result of executing lambda handler - * @param {string} code to assert in response body - * @param {Tap.Test} t - */ -function assertErrorBody({ result, code, t }) { - t.ok(result.body) - const jsonResult = JSON.parse(result.body) - t.ok(jsonResult) - t.ok(jsonResult.errors) - t.equal(jsonResult.errors.length, 1) // should have one parsing error - const [parseError] = jsonResult.errors - t.equal(parseError.extensions.code, code) - t.end() -} - -/** - * callback that verifies an error occurred - * and error result is as expected - * - * @param {Tap.Test} t - * @param {string} code to assert in response body - * @param {Error} err - * @param {*} result of lambda handler - */ -function errorCallback(t, code, err, result) { - t.error(err) - assertErrorBody({ result, code, t }) -} - -/** - * Executes a lambda handler and asserts an error occurred - * and result is as expected - * - * @param {Object} params - * @param {Function} params.handler to execute - * @param {string} params.query to execute - * @param {Object} params.context lambda context - * @param {string} params.modVersion version of lambda-server-lambda package - * @param {Tap.Test} params.t - * @param {string} params.code to assert in response body - */ -utils.executeQueryAssertErrors = async function executeQueryAssertErrors({ - handler, - query, - context, - modVersion, - t, - code -}) { - const jsonQuery = JSON.stringify({ query }) - const event = utils.createApiEvent(jsonQuery) - - if (requiresCallback(modVersion)) { - handler(event, context, errorCallback.bind(null, t, code)) - } else { - const result = await handler(event, context) - assertErrorBody({ result, code, t }) - } -} - -/** - * Creates a formatted API Gateway Proxy event - * to be used to execute a lambda handler - * - * @param {string} query to execute - */ -utils.createApiEvent = function createApiEvent(query) { - return { - path: '/graphql', - headers: { - 'Accept': 'application/json', - 'Accept-Encoding': 'gzip, deflate, lzma, sdch, br', - 'Accept-Language': 'en-US,en;q=0.8', - // must be lowercased as this is used in the bodyParser - // and assumes the header is lowercased - 'content-type': 'application/json', - 'CloudFront-Forwarded-Proto': 'https', - 'CloudFront-Is-Desktop-Viewer': 'true', - 'CloudFront-Is-Mobile-Viewer': 'false', - 'CloudFront-Is-SmartTV-Viewer': 'false', - 'CloudFront-Is-Tablet-Viewer': 'false', - 'CloudFront-Viewer-Country': 'US', - 'Host': 'wt6mne2s9k.execute-api.us-west-2.amazonaws.com', - 'Upgrade-Insecure-Requests': '1', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)', - 'Via': '1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)', - 'X-Amz-Cf-Id': 'nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==', - 'X-Forwarded-For': '192.168.100.1, 192.168.1.1', - 'X-Forwarded-Port': '443', - 'X-Forwarded-Proto': 'https' - }, - requestContext: { - accountId: '123456789012', - resourceId: 'us4z18', - stage: 'test', - requestId: '41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9', - identity: { - cognitoIdentityPoolId: '', - accountId: '', - cognitoIdentityId: '', - caller: '', - apiKey: '', - sourceIp: '192.168.100.1', - cognitoAuthenticationType: '', - cognitoAuthenticationProvider: '', - userArn: '', - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)', - user: '' - }, - resourcePath: '/{proxy+}', - httpMethod: 'GET', - apiId: 'wt6mne2s9k' - }, - resource: '/{proxy+}', - httpMethod: 'POST', - body: query - } -} diff --git a/tests/versioned/apollo-server-lambda/newrelic.js b/tests/versioned/apollo-server-lambda/newrelic.js deleted file mode 100644 index 1bce873..0000000 --- a/tests/versioned/apollo-server-lambda/newrelic.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -exports.config = { - app_name: ['My Application'], - license_key: 'license key here', - logging: { - level: 'trace', - filepath: '../../../newrelic_agent.log' - }, - utilization: { - detect_aws: false, - detect_pcf: false, - detect_azure: false, - detect_gcp: false, - detect_docker: false - }, - transaction_tracer: { - enabled: true - }, - serverless_mode: { - enabled: true - }, - distributed_tracing: { - enabled: true - } -} diff --git a/tests/versioned/apollo-server-lambda/package.json b/tests/versioned/apollo-server-lambda/package.json deleted file mode 100644 index 0b802d2..0000000 --- a/tests/versioned/apollo-server-lambda/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "apollo-server-tests", - "targets": [{"name":"apollo-server-lambda","minAgentVersion":"@newrelic/apollo-server-plugin@1.0.0"}], - "version": "0.0.0", - "private": true, - "tests": [ - { - "engines": { - "node": ">=16" - }, - "dependencies": { - "apollo-server-lambda": ">=2.14.0", - "graphql": "15.8.0" - }, - "files": [ - "segments.test.js", - "attributes-tests.js", - "transaction-naming-tests.js", - "errors-tests.js", - "query-obfuscation-tests.js" - ] - } - ], - "dependencies": {} -} diff --git a/tests/versioned/apollo-server-lambda/query-obfuscation-tests.js b/tests/versioned/apollo-server-lambda/query-obfuscation-tests.js deleted file mode 100644 index 12c3d7d..0000000 --- a/tests/versioned/apollo-server-lambda/query-obfuscation-tests.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { executeQueryAssertResult } = require('./lambda-test-utils') -const { findSegmentByName } = require('../../agent-testing') - -const SPAN_DESTINATION = 0x10 -const SEGMENT_DESTINATION = 0x20 - -const QUERY_ATTRIBUTE_NAME = 'graphql.operation.query' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -const { setupApolloServerLambdaTests } = require('./apollo-server-lambda-setup') - -setupApolloServerLambdaTests({ - suiteName: 'lambda query obfuscation', - createTests: createQueryObfuscationTests, - pluginConfig: { - captureScalars: true, - customResolverAttributes({ context }) { - return { - stage: context.event.requestContext.stage - } - } - } -}) - -function createQueryObfuscationTests(t) { - t.test('Obfuscates query arguments', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - library(branch: "rivers)i{de") { - magazines { - title - } - } - }` - - const path = 'library.magazines.title' - - helper.agent.on('transactionFinished', (transaction) => { - const operationName = `${OPERATION_PREFIX}/query/${expectedName}/${path}` - const operationSegment = findSegmentByName(transaction.trace.root, operationName) - - const operationAttributes = operationSegment.attributes.get(SEGMENT_DESTINATION) - - t.ok(operationAttributes[QUERY_ATTRIBUTE_NAME].includes('library(***)') > 0) - - const resolverName = `${RESOLVE_PREFIX}/library` - const resolverSegment = findSegmentByName(transaction.trace.root, resolverName) - const resolverCustomAttributes = resolverSegment - .getSpanContext() - .customAttributes.get(SPAN_DESTINATION) - t.match( - resolverCustomAttributes, - { stage: 'test' }, - 'should have a contextual custom attribute' - ) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) -} diff --git a/tests/versioned/apollo-server-lambda/segments.test.js b/tests/versioned/apollo-server-lambda/segments.test.js deleted file mode 100644 index dbffea6..0000000 --- a/tests/versioned/apollo-server-lambda/segments.test.js +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { - executeQueryAssertResult, - executeBatchAssertResult, - executeQueryAssertErrors -} = require('./lambda-test-utils') - -const ANON_PLACEHOLDER = '' -const UNKNOWN_OPERATION = '' - -const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer' -const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer' - -const { setupApolloServerLambdaTests } = require('./apollo-server-lambda-setup') - -setupApolloServerLambdaTests({ - suiteName: 'lambda segments', - createTests: createLambdaSegmentsTests, - pluginConfig: { - captureScalars: true - } -}) - -function createLambdaSegmentsTests(t, frameworkName) { - const TRANSACTION_PREFIX = `WebTransaction/${frameworkName}` - - t.test('anonymous query, single level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query, single level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/hello` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/hello` - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('anonymous query, multi-level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - libraries { - books { - author { - name - } - } - } - }` - - const path = 'libraries.books.author.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query, multi-level should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/libraries` }, - { name: `${RESOLVE_PREFIX}/libraries.books` }, - { name: `${RESOLVE_PREFIX}/libraries.books.title` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author` }, - { name: `${RESOLVE_PREFIX}/libraries.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query with aliases should use alias in segment naming', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - alias: libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { name: `${RESOLVE_PREFIX}/alias` }, - { name: `${RESOLVE_PREFIX}/alias.books` }, - { name: `${RESOLVE_PREFIX}/alias.books.title` }, - { name: `${RESOLVE_PREFIX}/alias.books.author` }, - { name: `${RESOLVE_PREFIX}/alias.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('anonymous mutation, single level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `mutation { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named mutation, single level, should use mutation name', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `mutation/${expectedName}/addThing` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('anonymous query, with params', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - paramQuery(blah: "blah", blee: "blee") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query, with params', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "blah") - }` - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/paramQuery` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/paramQuery` }] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query, with params, multi-level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const path = 'library.books' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query with fragment, query first', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - } - fragment LibraryBook on Book { - title - author { - name - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('named query with fragment, fragment first', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `fragment LibraryBook on Book { - title - author { - name - } - } - query ${expectedName} { - library(branch: "downtown") { - books { - ... LibraryBook - } - } - }` - - const path = 'library.books.LibraryBook' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('batch query should include segments for nested queries', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName1 = 'GetBookForLibrary' - const query1 = `query ${expectedName1} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const query2 = `mutation { - addThing(name: "added thing!") - }` - - const path1 = 'library.books' - - const queries = [query1, query2] - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart1 = `query/${expectedName1}/${path1}` - const expectedQuery1Name = `${operationPart1}` - const operationPart2 = `mutation/${ANON_PLACEHOLDER}/addThing` - const expectedQuery2Name = `${operationPart2}` - - const batchTransactionPrefix = `${TRANSACTION_PREFIX}//batch` - - const expectedSegments = [ - { - name: `${batchTransactionPrefix}/${expectedQuery1Name}/${expectedQuery2Name}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart1}`, - children: [ - { - name: `${RESOLVE_PREFIX}/library`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: ' - } - ] - } - ] - }, - { name: `${RESOLVE_PREFIX}/library.books` }, - { name: `${RESOLVE_PREFIX}/library.books.title` }, - { name: `${RESOLVE_PREFIX}/library.books.author` }, - { name: `${RESOLVE_PREFIX}/library.books.author.name` } - ] - }, - { - name: `${OPERATION_PREFIX}/${operationPart2}`, - children: [ - { - name: `${RESOLVE_PREFIX}/addThing`, - children: [ - { - name: 'timers.setTimeout', - children: [ - { - name: 'Callback: namedCallback' - } - ] - } - ] - } - ] - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeBatchAssertResult({ - handler: patchedHandler, - queries, - context: stubContext, - t, - modVersion - }) - }) - - t.test('union, single level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResult' - const query = `query ${expectedName} { - search(contains: "Ollies") { - __typename - ... on Book { - title - } - } - }` - - const deepestPath = 'search.title' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${deepestPath}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/search` }] - } - ] - } - ] - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - t.test('union, multiple inline fragments, single level', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResult' - const query = `query ${expectedName} { - search(contains: "Node") { - __typename - ... on Magazine { - title - } - ... on Book { - title - } - } - }` - - const deepestPath = 'search' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${expectedName}/${deepestPath}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}`, - children: [{ name: `${RESOLVE_PREFIX}/search` }] - } - ] - } - ] - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - t, - modVersion - }) - }) - - // there will be no document/AST nor resolved operation - t.test('when the query cannot be parsed, should have operation placeholder', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//*`, - children: [ - { - name: `${OPERATION_PREFIX}/${UNKNOWN_OPERATION}` - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_PARSE_FAILED' - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('when cannot validate, should include operation segment', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - const operationPart = `query/${ANON_PLACEHOLDER}/${path}` - const expectedSegments = [ - { - name: `${TRANSACTION_PREFIX}//${operationPart}`, - children: [ - { - name: `${OPERATION_PREFIX}/${operationPart}` - } - ] - } - ] - - t.segments(transaction.trace.root, expectedSegments) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_VALIDATION_FAILED' - }) - }) -} diff --git a/tests/versioned/apollo-server-lambda/transaction-naming-tests.js b/tests/versioned/apollo-server-lambda/transaction-naming-tests.js deleted file mode 100644 index 6285c3f..0000000 --- a/tests/versioned/apollo-server-lambda/transaction-naming-tests.js +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const { - createApiEvent, - executeBatchAssertResult, - executeQueryAssertResult, - executeQueryAssertErrors -} = require('./lambda-test-utils') -const { checkResult } = require('../common') - -const ANON_PLACEHOLDER = '' - -const { setupApolloServerLambdaTests } = require('./apollo-server-lambda-setup') - -setupApolloServerLambdaTests({ - suiteName: 'lambda transaction naming', - createTests: createTransactionTests, - pluginConfig: { - captureScalars: true - } -}) - -function createTransactionTests(t, frameworkName) { - const EXPECTED_PREFIX = `WebTransaction/${frameworkName}` - - t.test('anonymous query, single level, should use anonymous placeholder', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${ANON_PLACEHOLDER}/hello`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, single level, should use query name', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/hello`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('anonymous query, multi-level should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${ANON_PLACEHOLDER}/${path}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, multi-level should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libraries { - books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${path}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, multi-level with aliases should ignore aliases in naming', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBooksByLibrary' - const query = `query ${expectedName} { - libAlias: libraries { - bookAlias: books { - title - author { - name - } - } - } - }` - - const path = 'libraries.books' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${path}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('anonymous mutation, single level, should use anonymous placeholder', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `mutation { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//mutation/${ANON_PLACEHOLDER}/addThing`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named mutation, single level, should use mutation name', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'AddThing' - const query = `mutation ${expectedName} { - addThing(name: "added thing!") - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//mutation/${expectedName}/addThing`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('anonymous query, with params, should use anonymous placeholder', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const query = `query { - paramQuery(blah: "blah", blee: "blee") - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${ANON_PLACEHOLDER}/paramQuery`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, with params, should use query name', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'BlahQuery' - const query = `query ${expectedName} { - paramQuery(blah: "blah") - }` - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/paramQuery`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('named query, with params, should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetBookForLibrary' - const query = `query ${expectedName} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const path = 'library.books' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${path}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('batch query should include "batch" all queries separated by delimeter', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName1 = 'GetBookForLibrary' - const query1 = `query ${expectedName1} { - library(branch: "downtown") { - books { - title - author { - name - } - } - } - }` - - const query2 = `mutation { - addThing(name: "added thing!") - }` - - const path = 'library.books' - - const queries = [query1, query2] - - helper.agent.on('transactionFinished', (transaction) => { - const expectedQuery1Name = `query/${expectedName1}/${path}` - const expectedQuery2Name = `mutation/${ANON_PLACEHOLDER}/addThing` - t.equal( - transaction.name, - `${EXPECTED_PREFIX}//batch/${expectedQuery1Name}/${expectedQuery2Name}` - ) - }) - - executeBatchAssertResult({ - handler: patchedHandler, - queries, - context: stubContext, - modVersion, - t - }) - }) - - t.test('union, should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResults' - const query = `query ${expectedName} { - search(contains: "Ollies") { - __typename - ... on Book { - title - } - } - }` - - const deepestPath = 'search.title' - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${deepestPath}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - t.test('union, multiple inline fragments, should return deepest unique path', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'GetSearchResults' - const query = `query ${expectedName} { - search(contains: "Node") { - __typename - ... on Magazine { - title - } - ... on Book { - title - } - } - }` - - const deepestPath = 'search' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${deepestPath}`) - }) - - executeQueryAssertResult({ - handler: patchedHandler, - query, - context: stubContext, - modVersion, - t - }) - }) - - // there will be no document/AST nor resolved operation - t.test('if the query cannot be parsed, should be named /*', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const invalidQuery = `query { - libraries { - books { - title - author { - name - } - } - } - ` // missing closing } - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//*`) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_PARSE_FAILED' - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('anonymous query, when cant validate, should use document/AST', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const invalidQuery = `query { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${ANON_PLACEHOLDER}/${path}`) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_VALIDATION_FAILED' - }) - }) - - // if parse succeeds but validation fails, there will not be a resolved operation - // but the document/AST can still be leveraged for what was intended. - t.test('named query, when cant validate, should use document/AST', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'FailsToValidate' - const invalidQuery = `query ${expectedName} { - libraries { - books { - doesnotexist { - name - } - } - } - }` - - const path = 'libraries.books.doesnotexist.name' - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/${path}`) - }) - - executeQueryAssertErrors({ - handler: patchedHandler, - query: invalidQuery, - context: stubContext, - modVersion, - t, - code: 'GRAPHQL_VALIDATION_FAILED' - }) - }) - - t.test('multiple queries do not affect transaction naming', (t) => { - const { helper, patchedHandler, stubContext, modVersion } = t.context - - const expectedName = 'HeyThere' - const query = `query ${expectedName} { - hello - }` - let count = 0 - - helper.agent.on('transactionFinished', (transaction) => { - t.equal(transaction.name, `${EXPECTED_PREFIX}//query/${expectedName}/hello`) - count++ - }) - - const jsonQuery = JSON.stringify({ query }) - const event = createApiEvent(jsonQuery) - if (modVersion < '2.21.1') { - patchedHandler(event, stubContext, (err, result) => { - t.error(err) - checkResult(t, result, () => { - patchedHandler(event, stubContext, (err2, result2) => { - t.error(err2) - checkResult(t, result2, () => { - t.equal(count, 2, 'should have checked 2 transactions') - t.end() - }) - }) - }) - }) - } else { - patchedHandler(event, stubContext).then((result) => { - checkResult(t, result, async () => { - patchedHandler(event, stubContext).then((result2) => { - checkResult(t, result2, () => { - t.equal(count, 2, 'should have checked 2 transactions') - t.end() - }) - }) - }) - }) - } - }) -} diff --git a/tests/versioned/apollo-server/package.json b/tests/versioned/apollo-server/package.json index 6ab0d3c..8946192 100644 --- a/tests/versioned/apollo-server/package.json +++ b/tests/versioned/apollo-server/package.json @@ -15,7 +15,10 @@ "node": ">=16" }, "dependencies": { - "apollo-server": ">=2.14.0", + "apollo-server": { + "versions": ">=3.0.0", + "samples": 3 + }, "graphql": "15.8.0" }, "files": [ From 03c37b61e0395beeca335d90f8dea0c4b617e559 Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Wed, 24 Jul 2024 10:52:54 -0400 Subject: [PATCH 2/2] feat!: Dropped support for Node.js 16 (#316) --- .github/workflows/ci-workflow.yml | 6 +++--- package.json | 2 +- tests/versioned/apollo-federation/package.json | 2 +- tests/versioned/apollo-server-express/package.json | 6 +++--- tests/versioned/apollo-server/package.json | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 505d109..674878c 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -35,7 +35,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v3 @@ -61,7 +61,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v3 @@ -86,7 +86,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v3 diff --git a/package.json b/package.json index d8c2131..fbde792 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/newrelic/newrelic-node-apollo-server-plugin#readme", "engines": { - "node": ">=16.0.0" + "node": ">=18" }, "devDependencies": { "@apollo/server": "^4.1.1", diff --git a/tests/versioned/apollo-federation/package.json b/tests/versioned/apollo-federation/package.json index 1459c09..de801f0 100644 --- a/tests/versioned/apollo-federation/package.json +++ b/tests/versioned/apollo-federation/package.json @@ -6,7 +6,7 @@ "tests": [ { "engines": { - "node": ">=16" + "node": ">=18" }, "dependencies": { "@apollo/subgraph": "latest", diff --git a/tests/versioned/apollo-server-express/package.json b/tests/versioned/apollo-server-express/package.json index 5b87c88..edec261 100644 --- a/tests/versioned/apollo-server-express/package.json +++ b/tests/versioned/apollo-server-express/package.json @@ -4,12 +4,12 @@ "targets": [{"name":"apollo-server-express","minAgentVersion":"@newrelic/apollo-server-plugin@1.0.0"}], "private": true, "engines": { - "node": ">=16" + "node": ">=18" }, "tests": [ { "engines": { - "node": ">=16" + "node": ">=18" }, "dependencies": { "apollo-server-express": { @@ -30,7 +30,7 @@ }, { "engines": { - "node": ">=16" + "node": ">=18" }, "dependencies": { "@apollo/server": ">=4.0.0", diff --git a/tests/versioned/apollo-server/package.json b/tests/versioned/apollo-server/package.json index 8946192..3bb7324 100644 --- a/tests/versioned/apollo-server/package.json +++ b/tests/versioned/apollo-server/package.json @@ -7,12 +7,12 @@ "version": "0.0.0", "private": true, "engines": { - "node": ">=16" + "node": ">=18" }, "tests": [ { "engines": { - "node": ">=16" + "node": ">=18" }, "dependencies": { "apollo-server": { @@ -33,7 +33,7 @@ }, { "engines": { - "node": ">=16" + "node": ">=18" }, "dependencies": { "@apollo/server": ">=4.0.0",