From 849e890d312e98bb521bc6b4d03155228f300471 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Thu, 14 Apr 2022 17:43:13 +0200 Subject: [PATCH 01/12] test(tracer): refactor to pass runtime info + extract code to add a Lambda function --- packages/tracing/tests/e2e/constants.ts | 5 + packages/tracing/tests/e2e/tracer.test.ts | 884 +++++++++++----------- 2 files changed, 462 insertions(+), 427 deletions(-) create mode 100644 packages/tracing/tests/e2e/constants.ts diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts new file mode 100644 index 0000000000..f5b14c0935 --- /dev/null +++ b/packages/tracing/tests/e2e/constants.ts @@ -0,0 +1,5 @@ +export const RESOURCE_NAME_PREFIX = 'Tracer-E2E'; +export const ONE_MINUTE = 60 * 10_00; +export const TEST_CASE_TIMEOUT = ONE_MINUTE * 2; +export const SETUP_TIMEOUT = 300_000; +export const TEARDOWN_TIMEOUT = 200_000; \ No newline at end of file diff --git a/packages/tracing/tests/e2e/tracer.test.ts b/packages/tracing/tests/e2e/tracer.test.ts index f86bdc9531..15fc34ae47 100644 --- a/packages/tracing/tests/e2e/tracer.test.ts +++ b/packages/tracing/tests/e2e/tracer.test.ts @@ -1,7 +1,7 @@ /** * Test tracer manual mode * - * @group e2e/tracer/manual + * @group e2e/tracer/all */ import { randomUUID, randomBytes } from 'crypto'; @@ -13,14 +13,35 @@ import { App, Duration, Stack, RemovalPolicy } from 'aws-cdk-lib'; import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; import { getTraces, getInvocationSubsegment, splitSegmentsByName } from '../helpers/tracesUtils'; +import { SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT } from '../../../metrics/tests/e2e/constants'; +import { isValidRuntimeKey } from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; + +/** + * A type that contains information for invoking a Lambda function, + * and retrieving the traces. + * + * We fill the information while creting Lambda functions with CDK, + * and reuse it later in the test cases + */ +type InvocationMap = { + [key: string]: { + functionName: string + serviceName: string + resourceArn: string + } +}; const xray = new AWS.XRay(); const lambdaClient = new AWS.Lambda(); const stsClient = new AWS.STS(); -const ONE_MINUTE = 1000 * 60; +const runtime: string = process.env.RUNTIME || 'nodejs14x'; -describe('Tracer integration tests', () => { +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +describe(`Tracer E2E tests for runtime: ${runtime}`, () => { const expectedCustomAnnotationKey = 'myAnnotation'; const expectedCustomAnnotationValue = 'myValue'; @@ -33,43 +54,20 @@ describe('Tracer integration tests', () => { let integTestApp: App; let stack: Stack; - const invocationsMap: { [key: string]: { serviceName: string; functionName: string; resourceArn: string } } = {}; - - beforeAll(async () => { - - // Prepare - integTestApp = new App(); - stack = new Stack(integTestApp, 'TracerIntegTest'); + let invocationsMap: InvocationMap; + const addLambdaFunctions = async (stack: Stack, functionConfigs: string[], tableWithWriteAccess: Table): Promise => { + const region = process.env.AWS_REGION; const identity = await stsClient.getCallerIdentity().promise(); const account = identity.Account; - const region = process.env.AWS_REGION; - - const table = new Table(stack, 'Table', { - tableName: randomUUID(), - partitionKey: { - name: 'id', - type: AttributeType.STRING - }, - billingMode: BillingMode.PAY_PER_REQUEST, - removalPolicy: RemovalPolicy.DESTROY - }); - const functions = [ - 'Manual', - 'Middleware', - 'Middleware-Disabled', - 'Middleware-NoCaptureErrorResponse', - 'Decorator', - 'DecoratorWithAsyncHandler', - 'Decorator-Disabled', - 'Decorator-NoCaptureErrorResponse', - ]; - for (const functionName of functions) { + const map: InvocationMap = {}; + + for (const functionConfig of functionConfigs) { const expectedServiceName = randomUUID(); - const fileName = functionName.split('-')[0]; - const functionInstanceName = `${functionName}-${randomBytes(12).toString('hex')}`; - const fn = new NodejsFunction(stack, functionName, { + const fileName = functionConfig.split('-')[0]; + const functionInstanceName = `${functionConfig}-${randomBytes(12).toString('hex')}`; + const fn = new NodejsFunction(stack, functionConfig, { entry: join(__dirname, `tracer.test.${fileName}.ts`), handler: 'handler', functionName: functionInstanceName, @@ -84,23 +82,55 @@ describe('Tracer integration tests', () => { EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, - POWERTOOLS_TRACER_CAPTURE_RESPONSE: functionName.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', - POWERTOOLS_TRACER_CAPTURE_ERROR: functionName.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', - POWERTOOLS_TRACE_ENABLED: functionName.indexOf('Disabled') !== -1 ? 'false' : 'true', - TEST_TABLE_NAME: table.tableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', + POWERTOOLS_TRACE_ENABLED: functionConfig.indexOf('Disabled') !== -1 ? 'false' : 'true', + TEST_TABLE_NAME: tableWithWriteAccess.tableName, }, timeout: Duration.seconds(30), bundling: { externalModules: ['aws-sdk'], } }); - table.grantWriteData(fn); - invocationsMap[functionName] = { + tableWithWriteAccess.grantWriteData(fn); + + map[functionConfig] = { serviceName: expectedServiceName, functionName: functionInstanceName, resourceArn: `arn:aws:lambda:${region}:${account}:function:${functionInstanceName}`, // ARN is still a token at this point, so we construct the ARN manually }; } + + return map; + }; + + beforeAll(async () => { + + // Prepare + integTestApp = new App(); + stack = new Stack(integTestApp, 'TracerIntegTest'); // TODO: change stack name to be unique + + const table = new Table(stack, 'Table', { + tableName: randomUUID(), + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY + }); + + const functionConfigs = [ + 'Manual', + 'Middleware', + 'Middleware-Disabled', + 'Middleware-NoCaptureErrorResponse', + 'Decorator', + 'DecoratorWithAsyncHandler', + 'Decorator-Disabled', + 'Decorator-NoCaptureErrorResponse', + ]; + invocationsMap = await addLambdaFunctions(stack, functionConfigs, table); await deployStack(integTestApp, stack); @@ -119,7 +149,7 @@ describe('Tracer integration tests', () => { } }); - }, ONE_MINUTE * 5); + }, SETUP_TIMEOUT); afterAll(async () => { @@ -127,7 +157,7 @@ describe('Tracer integration tests', () => { await destroyStack(integTestApp, stack); } - }, ONE_MINUTE * 2); + }, TEARDOWN_TIMEOUT); it('Verifies that a when Tracer is used to manually instrument a function all custom traces are generated with correct annotations and metadata', async () => { @@ -199,453 +229,453 @@ describe('Tracer integration tests', () => { } } - }, ONE_MINUTE * 2); + }, TEST_CASE_TIMEOUT); - it('Verifies that a when Tracer is used as middleware all custom traces are generated with correct annotations and metadata', async () => { + // it('Verifies that a when Tracer is used as middleware all custom traces are generated with correct annotations and metadata', async () => { - const resourceArn = invocationsMap['Middleware'].resourceArn; - const expectedServiceName = invocationsMap['Middleware'].serviceName; + // const resourceArn = invocationsMap['Middleware'].resourceArn; + // const expectedServiceName = invocationsMap['Middleware'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(5); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); + // if (invocationSubsegment?.subsegments !== undefined) { + // expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); + // const handlerSubsegment = invocationSubsegment?.subsegments[0]; + // expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(3); - - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); + // if (handlerSubsegment?.subsegments !== undefined) { + // expect(handlerSubsegment?.subsegments?.length).toBe(3); + + // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + // // Assert that there are exactly two subsegment with the name 'DynamoDB' + // expect(subsegments.get('DynamoDB')?.length).toBe(2); + // // Assert that there is exactly one subsegment with the name 'httpbin.org' + // expect(subsegments.get('httpbin.org')?.length).toBe(1); + // // Assert that there are exactly zero other subsegments + // expect(subsegments.get('other')?.length).toBe(0); - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); + // const { annotations, metadata } = handlerSubsegment; + + // if (annotations !== undefined && metadata !== undefined) { + // // Assert that the annotations are as expected + // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + // expect(annotations['Service']).toEqual(expectedServiceName); + // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + // .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.fault).toBe(true); - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - } else { - // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) - .toEqual(expectedCustomResponseValue); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, ONE_MINUTE * 2); - - it('Verifies that a when Tracer is used as middleware, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // expect(handlerSubsegment.fault).toBe(true); + // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); + // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); + // } else { + // // Assert that the metadata object contains the response + // expect(metadata[expectedServiceName]['index.handler response']) + // .toEqual(expectedCustomResponseValue); + // } + // } else { + // // Make test fail if there are no annotations or metadata + // expect('annotations !== undefined && metadata !== undefined') + // .toBe('annotations === undefined && metadata === undefined'); + // } + // } else { + // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment + // expect('handlerSubsegment?.subsegments !== undefined') + // .toBe('handlerSubsegment?.subsegments === undefined'); + // } + // } else { + // // Make test fail if the Invocation subsegment doesn't have an handler subsebment + // expect('invocationSubsegment?.subsegments !== undefined') + // .toBe('invocationSubsegment?.subsegments === undefined'); + // } + // } + + // }, TEST_CASE_TIMEOUT); + + // it('Verifies that a when Tracer is used as middleware, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - const resourceArn = invocationsMap['Middleware-NoCaptureErrorResponse'].resourceArn; - const expectedServiceName = invocationsMap['Middleware-NoCaptureErrorResponse'].serviceName; + // const resourceArn = invocationsMap['Middleware-NoCaptureErrorResponse'].resourceArn; + // const expectedServiceName = invocationsMap['Middleware-NoCaptureErrorResponse'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(5); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); + // if (invocationSubsegment?.subsegments !== undefined) { + // expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); + // const handlerSubsegment = invocationSubsegment?.subsegments[0]; + // expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(3); - - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); + // if (handlerSubsegment?.subsegments !== undefined) { + // expect(handlerSubsegment?.subsegments?.length).toBe(3); + + // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + // // Assert that there are exactly two subsegment with the name 'DynamoDB' + // expect(subsegments.get('DynamoDB')?.length).toBe(2); + // // Assert that there is exactly one subsegment with the name 'httpbin.org' + // expect(subsegments.get('httpbin.org')?.length).toBe(1); + // // Assert that there are exactly zero other subsegments + // expect(subsegments.get('other')?.length).toBe(0); - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); + // const { annotations, metadata } = handlerSubsegment; + + // if (annotations !== undefined && metadata !== undefined) { + // // Assert that the annotations are as expected + // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + // expect(annotations['Service']).toEqual(expectedServiceName); + // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + // .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.error).toBe(true); - // Assert that no error was captured on the subsegment - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); - } else { - // Assert that the metadata object does not contain the response object - expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, ONE_MINUTE * 2); - - it('Verifies that a when tracing is disabled in middleware mode no custom traces are generated', async () => { + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // expect(handlerSubsegment.error).toBe(true); + // // Assert that no error was captured on the subsegment + // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); + // } else { + // // Assert that the metadata object does not contain the response object + // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); + // } + // } else { + // // Make test fail if there are no annotations or metadata + // expect('annotations !== undefined && metadata !== undefined') + // .toBe('annotations === undefined && metadata === undefined'); + // } + // } else { + // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment + // expect('handlerSubsegment?.subsegments !== undefined') + // .toBe('handlerSubsegment?.subsegments === undefined'); + // } + // } else { + // // Make test fail if the Invocation subsegment doesn't have an handler subsebment + // expect('invocationSubsegment?.subsegments !== undefined') + // .toBe('invocationSubsegment?.subsegments === undefined'); + // } + // } + + // }, TEST_CASE_TIMEOUT); + + // it('Verifies that a when tracing is disabled in middleware mode no custom traces are generated', async () => { - const resourceArn = invocationsMap['Middleware-Disabled'].resourceArn; + // const resourceArn = invocationsMap['Middleware-Disabled'].resourceArn; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(2); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(2); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - expect(invocationSubsegment?.subsegments).toBeUndefined(); + // expect(invocationSubsegment?.subsegments).toBeUndefined(); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - } - } + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // } + // } - }, ONE_MINUTE * 2); + // }, TEST_CASE_TIMEOUT); - it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { + // it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { - const resourceArn = invocationsMap['Decorator'].resourceArn; - const expectedServiceName = invocationsMap['Decorator'].serviceName; + // const resourceArn = invocationsMap['Decorator'].resourceArn; + // const expectedServiceName = invocationsMap['Decorator'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(5); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); + // if (invocationSubsegment?.subsegments !== undefined) { + // expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); + // const handlerSubsegment = invocationSubsegment?.subsegments[0]; + // expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(4); + // if (handlerSubsegment?.subsegments !== undefined) { + // expect(handlerSubsegment?.subsegments?.length).toBe(4); - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there is exactly one subsegment with the name '### myMethod' - expect(subsegments.get('### myMethod')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); - - const methodSubsegment = subsegments.get('### myMethod') || []; - const { metadata } = methodSubsegment[0]; - - if (metadata !== undefined) { - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName]['myMethod response']) - .toEqual(expectedCustomResponseValue); - } else { - // Make test fail if there is no metadata - expect('metadata !== undefined') - .toBe('metadata === undefined'); - } - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } + // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + // // Assert that there are exactly two subsegment with the name 'DynamoDB' + // expect(subsegments.get('DynamoDB')?.length).toBe(2); + // // Assert that there is exactly one subsegment with the name 'httpbin.org' + // expect(subsegments.get('httpbin.org')?.length).toBe(1); + // // Assert that there is exactly one subsegment with the name '### myMethod' + // expect(subsegments.get('### myMethod')?.length).toBe(1); + // // Assert that there are exactly zero other subsegments + // expect(subsegments.get('other')?.length).toBe(0); + + // const methodSubsegment = subsegments.get('### myMethod') || []; + // const { metadata } = methodSubsegment[0]; + + // if (metadata !== undefined) { + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName]['myMethod response']) + // .toEqual(expectedCustomResponseValue); + // } else { + // // Make test fail if there is no metadata + // expect('metadata !== undefined') + // .toBe('metadata === undefined'); + // } + // } else { + // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment + // expect('handlerSubsegment?.subsegments !== undefined') + // .toBe('handlerSubsegment?.subsegments === undefined'); + // } - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); + // const { annotations, metadata } = handlerSubsegment; + + // if (annotations !== undefined && metadata !== undefined) { + // // Assert that the annotations are as expected + // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + // expect(annotations['Service']).toEqual(expectedServiceName); + // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + // .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.fault).toBe(true); - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - } else { - // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) - .toEqual(expectedCustomResponseValue); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, ONE_MINUTE * 2); - - it('Verifies that a when Tracer is used as decorator on an async handler all custom traces are generated with correct annotations and metadata', async () => { + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // expect(handlerSubsegment.fault).toBe(true); + // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); + // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); + // } else { + // // Assert that the metadata object contains the response + // expect(metadata[expectedServiceName]['index.handler response']) + // .toEqual(expectedCustomResponseValue); + // } + // } else { + // // Make test fail if there are no annotations or metadata + // expect('annotations !== undefined && metadata !== undefined') + // .toBe('annotations === undefined && metadata === undefined'); + // } + // } else { + // // Make test fail if the Invocation subsegment doesn't have an handler subsebment + // expect('invocationSubsegment?.subsegments !== undefined') + // .toBe('invocationSubsegment?.subsegments === undefined'); + // } + // } + + // }, TEST_CASE_TIMEOUT); + + // it('Verifies that a when Tracer is used as decorator on an async handler all custom traces are generated with correct annotations and metadata', async () => { - const resourceArn = invocationsMap['DecoratorWithAsyncHandler'].resourceArn; - const expectedServiceName = invocationsMap['DecoratorWithAsyncHandler'].serviceName; + // const resourceArn = invocationsMap['DecoratorWithAsyncHandler'].resourceArn; + // const expectedServiceName = invocationsMap['DecoratorWithAsyncHandler'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(5); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); + // if (invocationSubsegment?.subsegments !== undefined) { + // expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); + // const handlerSubsegment = invocationSubsegment?.subsegments[0]; + // expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(4); + // if (handlerSubsegment?.subsegments !== undefined) { + // expect(handlerSubsegment?.subsegments?.length).toBe(4); - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there is exactly one subsegment with the name '### myMethod' - expect(subsegments.get('### myMethod')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); - - const methodSubsegment = subsegments.get('### myMethod') || []; - const { metadata } = methodSubsegment[0]; - - if (metadata !== undefined) { - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName]['myMethod response']) - .toEqual(expectedCustomResponseValue); - } else { - // Make test fail if there is no metadata - expect('metadata !== undefined') - .toBe('metadata === undefined'); - } - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } + // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + // // Assert that there are exactly two subsegment with the name 'DynamoDB' + // expect(subsegments.get('DynamoDB')?.length).toBe(2); + // // Assert that there is exactly one subsegment with the name 'httpbin.org' + // expect(subsegments.get('httpbin.org')?.length).toBe(1); + // // Assert that there is exactly one subsegment with the name '### myMethod' + // expect(subsegments.get('### myMethod')?.length).toBe(1); + // // Assert that there are exactly zero other subsegments + // expect(subsegments.get('other')?.length).toBe(0); + + // const methodSubsegment = subsegments.get('### myMethod') || []; + // const { metadata } = methodSubsegment[0]; + + // if (metadata !== undefined) { + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName]['myMethod response']) + // .toEqual(expectedCustomResponseValue); + // } else { + // // Make test fail if there is no metadata + // expect('metadata !== undefined') + // .toBe('metadata === undefined'); + // } + // } else { + // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment + // expect('handlerSubsegment?.subsegments !== undefined') + // .toBe('handlerSubsegment?.subsegments === undefined'); + // } - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); + // const { annotations, metadata } = handlerSubsegment; + + // if (annotations !== undefined && metadata !== undefined) { + // // Assert that the annotations are as expected + // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + // expect(annotations['Service']).toEqual(expectedServiceName); + // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + // .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.fault).toBe(true); - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - } else { - // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) - .toEqual(expectedCustomResponseValue); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, ONE_MINUTE * 2); - - it('Verifies that a when Tracer is used as decorator, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // expect(handlerSubsegment.fault).toBe(true); + // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); + // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); + // } else { + // // Assert that the metadata object contains the response + // expect(metadata[expectedServiceName]['index.handler response']) + // .toEqual(expectedCustomResponseValue); + // } + // } else { + // // Make test fail if there are no annotations or metadata + // expect('annotations !== undefined && metadata !== undefined') + // .toBe('annotations === undefined && metadata === undefined'); + // } + // } else { + // // Make test fail if the Invocation subsegment doesn't have an handler subsebment + // expect('invocationSubsegment?.subsegments !== undefined') + // .toBe('invocationSubsegment?.subsegments === undefined'); + // } + // } + + // }, TEST_CASE_TIMEOUT); + + // it('Verifies that a when Tracer is used as decorator, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - const resourceArn = invocationsMap['Decorator-NoCaptureErrorResponse'].resourceArn; - const expectedServiceName = invocationsMap['Decorator-NoCaptureErrorResponse'].serviceName; + // const resourceArn = invocationsMap['Decorator-NoCaptureErrorResponse'].resourceArn; + // const expectedServiceName = invocationsMap['Decorator-NoCaptureErrorResponse'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(5); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); + // if (invocationSubsegment?.subsegments !== undefined) { + // expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); + // const handlerSubsegment = invocationSubsegment?.subsegments[0]; + // expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(4); + // if (handlerSubsegment?.subsegments !== undefined) { + // expect(handlerSubsegment?.subsegments?.length).toBe(4); - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there is exactly one subsegment with the name '### myMethod' - expect(subsegments.get('### myMethod')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); - - // Assert that no response was captured on the subsegment - const methodSubsegment = subsegments.get('### myMethod') || []; - expect(methodSubsegment[0].hasOwnProperty('metadata')).toBe(false); - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } + // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + // // Assert that there are exactly two subsegment with the name 'DynamoDB' + // expect(subsegments.get('DynamoDB')?.length).toBe(2); + // // Assert that there is exactly one subsegment with the name 'httpbin.org' + // expect(subsegments.get('httpbin.org')?.length).toBe(1); + // // Assert that there is exactly one subsegment with the name '### myMethod' + // expect(subsegments.get('### myMethod')?.length).toBe(1); + // // Assert that there are exactly zero other subsegments + // expect(subsegments.get('other')?.length).toBe(0); + + // // Assert that no response was captured on the subsegment + // const methodSubsegment = subsegments.get('### myMethod') || []; + // expect(methodSubsegment[0].hasOwnProperty('metadata')).toBe(false); + // } else { + // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment + // expect('handlerSubsegment?.subsegments !== undefined') + // .toBe('handlerSubsegment?.subsegments === undefined'); + // } - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); + // const { annotations, metadata } = handlerSubsegment; + + // if (annotations !== undefined && metadata !== undefined) { + // // Assert that the annotations are as expected + // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + // expect(annotations['Service']).toEqual(expectedServiceName); + // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // // Assert that the metadata object is as expected + // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + // .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.error).toBe(true); - // Assert that no error was captured on the subsegment - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); - } else { - // Assert that the metadata object does not contain the response object - expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, ONE_MINUTE * 2); - - it('Verifies that a when tracing is disabled in decorator mode no custom traces are generated', async () => { + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // expect(handlerSubsegment.error).toBe(true); + // // Assert that no error was captured on the subsegment + // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); + // } else { + // // Assert that the metadata object does not contain the response object + // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); + // } + // } else { + // // Make test fail if there are no annotations or metadata + // expect('annotations !== undefined && metadata !== undefined') + // .toBe('annotations === undefined && metadata === undefined'); + // } + // } else { + // // Make test fail if the Invocation subsegment doesn't have an handler subsebment + // expect('invocationSubsegment?.subsegments !== undefined') + // .toBe('invocationSubsegment?.subsegments === undefined'); + // } + // } + + // }, TEST_CASE_TIMEOUT); + + // it('Verifies that a when tracing is disabled in decorator mode no custom traces are generated', async () => { - const resourceArn = invocationsMap['Decorator-Disabled'].resourceArn; + // const resourceArn = invocationsMap['Decorator-Disabled'].resourceArn; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); + // // Assess + // // Retrieve traces from X-Ray using Resource ARN as filter + // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - for (let i = 0; i < invocations; i++) { - // Assert that the trace has the expected amount of segments - expect(sortedTraces[i].Segments.length).toBe(2); + // for (let i = 0; i < invocations; i++) { + // // Assert that the trace has the expected amount of segments + // expect(sortedTraces[i].Segments.length).toBe(2); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); + // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - expect(invocationSubsegment?.subsegments).toBeUndefined(); + // expect(invocationSubsegment?.subsegments).toBeUndefined(); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - } - } + // if (i === invocations - 1) { + // // Assert that the subsegment has the expected fault + // expect(invocationSubsegment.error).toBe(true); + // } + // } - }, ONE_MINUTE * 2); + // }, TEST_CASE_TIMEOUT); }); \ No newline at end of file From 0f1e56cf7e7eb5876d372eca890e3ea64cba4fd7 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Thu, 14 Apr 2022 17:51:44 +0200 Subject: [PATCH 02/12] test(tracer): refactor to extract common used values out --- packages/tracing/tests/e2e/constants.ts | 25 ++++++++++++++++- packages/tracing/tests/e2e/tracer.test.ts | 34 ++++++++--------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts index f5b14c0935..bbf26bf26b 100644 --- a/packages/tracing/tests/e2e/constants.ts +++ b/packages/tracing/tests/e2e/constants.ts @@ -2,4 +2,27 @@ export const RESOURCE_NAME_PREFIX = 'Tracer-E2E'; export const ONE_MINUTE = 60 * 10_00; export const TEST_CASE_TIMEOUT = ONE_MINUTE * 2; export const SETUP_TIMEOUT = 300_000; -export const TEARDOWN_TIMEOUT = 200_000; \ No newline at end of file +export const TEARDOWN_TIMEOUT = 200_000; + +export const expectedCustomAnnotationKey = 'myAnnotation'; +export const expectedCustomAnnotationValue = 'myValue'; +export const expectedCustomMetadataKey = 'myMetadata'; +export const expectedCustomMetadataValue = { bar: 'baz' }; +export const expectedCustomResponseValue = { foo: 'bar' }; +export const expectedCustomErrorMessage = 'An error has occurred'; + + +/** + * A type that contains information for invoking a Lambda function, + * and retrieving the traces. + * + * We fill the information while creting Lambda functions with CDK, + * and reuse it later in the test cases + */ +export type InvocationMap = { + [key: string]: { + functionName: string + serviceName: string + resourceArn: string + } +}; \ No newline at end of file diff --git a/packages/tracing/tests/e2e/tracer.test.ts b/packages/tracing/tests/e2e/tracer.test.ts index 15fc34ae47..1bc0464ff5 100644 --- a/packages/tracing/tests/e2e/tracer.test.ts +++ b/packages/tracing/tests/e2e/tracer.test.ts @@ -13,23 +13,19 @@ import { App, Duration, Stack, RemovalPolicy } from 'aws-cdk-lib'; import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; import { getTraces, getInvocationSubsegment, splitSegmentsByName } from '../helpers/tracesUtils'; -import { SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT } from '../../../metrics/tests/e2e/constants'; import { isValidRuntimeKey } from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; - -/** - * A type that contains information for invoking a Lambda function, - * and retrieving the traces. - * - * We fill the information while creting Lambda functions with CDK, - * and reuse it later in the test cases - */ -type InvocationMap = { - [key: string]: { - functionName: string - serviceName: string - resourceArn: string - } -}; +import { + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, + expectedCustomErrorMessage, + InvocationMap, +} from './constants'; const xray = new AWS.XRay(); const lambdaClient = new AWS.Lambda(); @@ -43,12 +39,6 @@ if (!isValidRuntimeKey(runtime)) { describe(`Tracer E2E tests for runtime: ${runtime}`, () => { - const expectedCustomAnnotationKey = 'myAnnotation'; - const expectedCustomAnnotationValue = 'myValue'; - const expectedCustomMetadataKey = 'myMetadata'; - const expectedCustomMetadataValue = { bar: 'baz' }; - const expectedCustomResponseValue = { foo: 'bar' }; - const expectedCustomErrorMessage = 'An error has occurred'; const startTime = new Date(); const invocations = 3; From 33e8c701b0994395d42dd7a6e849f6cc538890bb Mon Sep 17 00:00:00 2001 From: ijemmy Date: Tue, 19 Apr 2022 16:21:44 +0200 Subject: [PATCH 03/12] test: refactor to take manual test out in its own separated file --- packages/commons/tests/utils/e2eUtils.ts | 2 +- ...> allFeatures.manual.test.functionCode.ts} | 0 .../tests/e2e/allFeatures.manual.test.ts | 222 ++++ packages/tracing/tests/e2e/constants.ts | 7 +- packages/tracing/tests/e2e/tracer.test.ts | 1182 ++++++++--------- packages/tracing/tests/helpers/tracesUtils.ts | 2 +- 6 files changed, 818 insertions(+), 597 deletions(-) rename packages/tracing/tests/e2e/{tracer.test.Manual.ts => allFeatures.manual.test.functionCode.ts} (100%) create mode 100644 packages/tracing/tests/e2e/allFeatures.manual.test.ts diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index 00143f4788..805b06f6e8 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -16,7 +16,7 @@ const lambdaClient = new AWS.Lambda(); const testRuntimeKeys = [ 'nodejs12x', 'nodejs14x' ]; export type TestRuntimesKey = typeof testRuntimeKeys[number]; -const TEST_RUNTIMES: Record = { +export const TEST_RUNTIMES: Record = { nodejs12x: Runtime.NODEJS_12_X, nodejs14x: Runtime.NODEJS_14_X, }; diff --git a/packages/tracing/tests/e2e/tracer.test.Manual.ts b/packages/tracing/tests/e2e/allFeatures.manual.test.functionCode.ts similarity index 100% rename from packages/tracing/tests/e2e/tracer.test.Manual.ts rename to packages/tracing/tests/e2e/allFeatures.manual.test.functionCode.ts diff --git a/packages/tracing/tests/e2e/allFeatures.manual.test.ts b/packages/tracing/tests/e2e/allFeatures.manual.test.ts new file mode 100644 index 0000000000..18c2aa746b --- /dev/null +++ b/packages/tracing/tests/e2e/allFeatures.manual.test.ts @@ -0,0 +1,222 @@ +/** + * Test tracer manual mode + * + * @group e2e/tracer/manual + */ + +import { randomUUID } from 'crypto'; +import path from 'path'; +import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +import { App, Stack, RemovalPolicy, Duration } from 'aws-cdk-lib'; +import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; +import * as AWS from 'aws-sdk'; +import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace } from '../helpers/tracesUtils'; +import { + generateUniqueName, + isValidRuntimeKey, + TestRuntimesKey, + TEST_RUNTIMES, +} from '../../../commons/tests/utils/e2eUtils'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, + expectedCustomErrorMessage, +} from './constants'; +import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; + +const runtime: string = process.env.RUNTIME || 'nodejs14x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const uuid = randomUUID(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Manual'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Manual'); +const lambdaFunctionCodeFile = 'allFeatures.manual.test.functionCode.ts'; +const expectedServiceName = functionName; +let startTime : Date; + +const lambdaClient = new AWS.Lambda(); +const xray = new AWS.XRay(); +const invocations = 3; +let sortedTraces: ParsedTrace[]; + +const integTestApp = new App(); +let stack: Stack; + +const getFunctionArn = async (functionName: string): Promise => { + const stsClient = new AWS.STS(); + const region = process.env.AWS_REGION; + const identity = await stsClient.getCallerIdentity().promise(); + const account = identity.Account; + + return `arn:aws:lambda:${region}:${account}:function:${functionName}`; +}; + +describe(`Tracer E2E tests, all features with manual instantiation for runtime: ${runtime}`, () => { + + beforeAll(async () => { + + // Prepare + startTime = new Date(); + const ddbTableName = stackName + '-table'; + stack = new Stack(integTestApp, stackName); + + const testFunction = new NodejsFunction(stack, functionName, { + entry: path.join(__dirname, lambdaFunctionCodeFile), + functionName: functionName, + handler: 'handler', + tracing: Tracing.ACTIVE, + architecture: Architecture.X86_64, + memorySize: 256, + environment: { + EXPECTED_SERVICE_NAME: expectedServiceName, + EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, + EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, + EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, + EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), + EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), + EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + TEST_TABLE_NAME: ddbTableName, + }, + timeout: Duration.seconds(30), + bundling: { + externalModules: ['aws-sdk'], + }, + runtime: TEST_RUNTIMES[runtime as TestRuntimesKey], + }); + const ddbTable = new Table(stack, 'Table', { + tableName: ddbTableName, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY + }); + + ddbTable.grantWriteData(testFunction); + + await deployStack(integTestApp, stack); + + // Act + for (let i = 0; i < invocations; i++) { + await lambdaClient.invoke({ + FunctionName: functionName, + LogType: 'Tail', + Payload: JSON.stringify({ + throw: i === invocations - 1 ? true : false, // only last invocation should throw + sdkV2: i === 1 ? 'all' : 'client', // only second invocation should use captureAll + invocation: i + 1, // Pass invocation number for easier debugging + }), + }).promise(); + } + + // Retrieve traces from X-Ray for assertion + const lambdaFunctionArn = await getFunctionArn(functionName); + sortedTraces = await getTraces(xray, startTime, lambdaFunctionArn, invocations, 5); + + }, SETUP_TIMEOUT); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + + it('should generate all custom traces with correct annotations and metadata', async () => { + + expect(sortedTraces.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = sortedTraces[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + + const invocationSubsegment = getInvocationSubsegment(trace); + + if (invocationSubsegment?.subsegments !== undefined) { + expect(invocationSubsegment?.subsegments?.length).toBe(1); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + */ + const handlerSubsegment = invocationSubsegment?.subsegments[0]; + expect(handlerSubsegment.name).toBe('## index.handler'); + + if (handlerSubsegment?.subsegments !== undefined) { + /** + * '## index.handler' subsegment should have 3 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + */ + expect(handlerSubsegment?.subsegments?.length).toBe(3); + + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + // Assert that there are exactly two subsegment with the name 'DynamoDB' + expect(subsegments.get('DynamoDB')?.length).toBe(2); + // Assert that there is exactly one subsegment with the name 'httpbin.org' + expect(subsegments.get('httpbin.org')?.length).toBe(1); + // Assert that there are exactly zero other subsegments + expect(subsegments.get('other')?.length).toBe(0); + + const { annotations, metadata } = handlerSubsegment; + + if (annotations !== undefined && metadata !== undefined) { + // Assert that the annotations are as expected + expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); + expect(annotations['Service']).toEqual(expectedServiceName); + expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); + // Assert that the metadata object is as expected + expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + .toEqual(expectedCustomMetadataValue); + + if (i === invocations - 1) { + // Assert that the subsegment has the expected fault + expect(invocationSubsegment.error).toBe(true); + expect(handlerSubsegment.fault).toBe(true); + expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); + expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); + } else { + // Assert that the metadata object contains the response + expect(metadata[expectedServiceName]['index.handler response']) + .toEqual(expectedCustomResponseValue); + } + } else { + // Make test fail if there are no annotations or metadata + fail('There is no annotations or metadata in the handler subsgement') + } + } else { + fail('Handler subsegment does NOT have any subsebments'); + } + } else { + fail('Invocation subsegment does NOT have a handler subsebment'); + } + } + + }, TEST_CASE_TIMEOUT); + +}); + diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts index bbf26bf26b..5b4fbc5f0a 100644 --- a/packages/tracing/tests/e2e/constants.ts +++ b/packages/tracing/tests/e2e/constants.ts @@ -1,8 +1,8 @@ export const RESOURCE_NAME_PREFIX = 'Tracer-E2E'; -export const ONE_MINUTE = 60 * 10_00; +export const ONE_MINUTE = 60 * 1_000; export const TEST_CASE_TIMEOUT = ONE_MINUTE * 2; -export const SETUP_TIMEOUT = 300_000; -export const TEARDOWN_TIMEOUT = 200_000; +export const SETUP_TIMEOUT = ONE_MINUTE * 5; +export const TEARDOWN_TIMEOUT = ONE_MINUTE * 3; export const expectedCustomAnnotationKey = 'myAnnotation'; export const expectedCustomAnnotationValue = 'myValue'; @@ -11,7 +11,6 @@ export const expectedCustomMetadataValue = { bar: 'baz' }; export const expectedCustomResponseValue = { foo: 'bar' }; export const expectedCustomErrorMessage = 'An error has occurred'; - /** * A type that contains information for invoking a Lambda function, * and retrieving the traces. diff --git a/packages/tracing/tests/e2e/tracer.test.ts b/packages/tracing/tests/e2e/tracer.test.ts index 1bc0464ff5..5c32204b67 100644 --- a/packages/tracing/tests/e2e/tracer.test.ts +++ b/packages/tracing/tests/e2e/tracer.test.ts @@ -1,671 +1,671 @@ -/** - * Test tracer manual mode - * - * @group e2e/tracer/all - */ - -import { randomUUID, randomBytes } from 'crypto'; -import { join } from 'path'; -import { Tracing, Architecture } from 'aws-cdk-lib/aws-lambda'; -import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; -import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Duration, Stack, RemovalPolicy } from 'aws-cdk-lib'; -import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; -import * as AWS from 'aws-sdk'; -import { getTraces, getInvocationSubsegment, splitSegmentsByName } from '../helpers/tracesUtils'; -import { isValidRuntimeKey } from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; -import { - SETUP_TIMEOUT, - TEARDOWN_TIMEOUT, - TEST_CASE_TIMEOUT, - expectedCustomAnnotationKey, - expectedCustomAnnotationValue, - expectedCustomMetadataKey, - expectedCustomMetadataValue, - expectedCustomResponseValue, - expectedCustomErrorMessage, - InvocationMap, -} from './constants'; - -const xray = new AWS.XRay(); -const lambdaClient = new AWS.Lambda(); -const stsClient = new AWS.STS(); - -const runtime: string = process.env.RUNTIME || 'nodejs14x'; - -if (!isValidRuntimeKey(runtime)) { - throw new Error(`Invalid runtime key value: ${runtime}`); -} - -describe(`Tracer E2E tests for runtime: ${runtime}`, () => { - - const startTime = new Date(); - const invocations = 3; - - let integTestApp: App; - let stack: Stack; - let invocationsMap: InvocationMap; - - const addLambdaFunctions = async (stack: Stack, functionConfigs: string[], tableWithWriteAccess: Table): Promise => { - const region = process.env.AWS_REGION; - const identity = await stsClient.getCallerIdentity().promise(); - const account = identity.Account; - - const map: InvocationMap = {}; - - for (const functionConfig of functionConfigs) { - const expectedServiceName = randomUUID(); - const fileName = functionConfig.split('-')[0]; - const functionInstanceName = `${functionConfig}-${randomBytes(12).toString('hex')}`; - const fn = new NodejsFunction(stack, functionConfig, { - entry: join(__dirname, `tracer.test.${fileName}.ts`), - handler: 'handler', - functionName: functionInstanceName, - tracing: Tracing.ACTIVE, - architecture: Architecture.X86_64, - memorySize: 256, - environment: { - EXPECTED_SERVICE_NAME: expectedServiceName, - EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, - EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, - EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, - EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), - EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), - EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, - POWERTOOLS_TRACER_CAPTURE_RESPONSE: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', - POWERTOOLS_TRACER_CAPTURE_ERROR: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', - POWERTOOLS_TRACE_ENABLED: functionConfig.indexOf('Disabled') !== -1 ? 'false' : 'true', - TEST_TABLE_NAME: tableWithWriteAccess.tableName, - }, - timeout: Duration.seconds(30), - bundling: { - externalModules: ['aws-sdk'], - } - }); - tableWithWriteAccess.grantWriteData(fn); - - map[functionConfig] = { - serviceName: expectedServiceName, - functionName: functionInstanceName, - resourceArn: `arn:aws:lambda:${region}:${account}:function:${functionInstanceName}`, // ARN is still a token at this point, so we construct the ARN manually - }; - } +// /** +// * Test tracer manual mode +// * +// * @group e2e/tracer/all +// */ + +// import { randomUUID, randomBytes } from 'crypto'; +// import { join } from 'path'; +// import { Tracing, Architecture } from 'aws-cdk-lib/aws-lambda'; +// import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +// import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +// import { App, Duration, Stack, RemovalPolicy } from 'aws-cdk-lib'; +// import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +// import * as AWS from 'aws-sdk'; +// import { getTraces, getInvocationSubsegment, splitSegmentsByName } from '../helpers/tracesUtils'; +// import { isValidRuntimeKey } from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +// import { +// SETUP_TIMEOUT, +// TEARDOWN_TIMEOUT, +// TEST_CASE_TIMEOUT, +// expectedCustomAnnotationKey, +// expectedCustomAnnotationValue, +// expectedCustomMetadataKey, +// expectedCustomMetadataValue, +// expectedCustomResponseValue, +// expectedCustomErrorMessage, +// InvocationMap, +// } from './constants'; + +// const xray = new AWS.XRay(); +// const lambdaClient = new AWS.Lambda(); +// const stsClient = new AWS.STS(); + +// const runtime: string = process.env.RUNTIME || 'nodejs14x'; + +// if (!isValidRuntimeKey(runtime)) { +// throw new Error(`Invalid runtime key value: ${runtime}`); +// } + +// describe(`Tracer E2E tests for runtime: ${runtime}`, () => { + +// const startTime = new Date(); +// const invocations = 3; + +// let integTestApp: App; +// let stack: Stack; +// let invocationsMap: InvocationMap; + +// const addLambdaFunctions = async (stack: Stack, functionConfigs: string[], tableWithWriteAccess: Table): Promise => { +// const region = process.env.AWS_REGION; +// const identity = await stsClient.getCallerIdentity().promise(); +// const account = identity.Account; + +// const map: InvocationMap = {}; + +// for (const functionConfig of functionConfigs) { +// const expectedServiceName = randomUUID(); +// const fileName = functionConfig.split('-')[0]; +// const functionInstanceName = `${functionConfig}-${randomBytes(12).toString('hex')}`; +// const fn = new NodejsFunction(stack, functionConfig, { +// entry: join(__dirname, `tracer.test.${fileName}.ts`), +// handler: 'handler', +// functionName: functionInstanceName, +// tracing: Tracing.ACTIVE, +// architecture: Architecture.X86_64, +// memorySize: 256, +// environment: { +// EXPECTED_SERVICE_NAME: expectedServiceName, +// EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, +// EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, +// EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, +// EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), +// EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), +// EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, +// POWERTOOLS_TRACER_CAPTURE_RESPONSE: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', +// POWERTOOLS_TRACER_CAPTURE_ERROR: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', +// POWERTOOLS_TRACE_ENABLED: functionConfig.indexOf('Disabled') !== -1 ? 'false' : 'true', +// TEST_TABLE_NAME: tableWithWriteAccess.tableName, +// }, +// timeout: Duration.seconds(30), +// bundling: { +// externalModules: ['aws-sdk'], +// } +// }); +// tableWithWriteAccess.grantWriteData(fn); + +// map[functionConfig] = { +// serviceName: expectedServiceName, +// functionName: functionInstanceName, +// resourceArn: `arn:aws:lambda:${region}:${account}:function:${functionInstanceName}`, // ARN is still a token at this point, so we construct the ARN manually +// }; +// } - return map; - }; +// return map; +// }; - beforeAll(async () => { - - // Prepare - integTestApp = new App(); - stack = new Stack(integTestApp, 'TracerIntegTest'); // TODO: change stack name to be unique - - const table = new Table(stack, 'Table', { - tableName: randomUUID(), - partitionKey: { - name: 'id', - type: AttributeType.STRING - }, - billingMode: BillingMode.PAY_PER_REQUEST, - removalPolicy: RemovalPolicy.DESTROY - }); - - const functionConfigs = [ - 'Manual', - 'Middleware', - 'Middleware-Disabled', - 'Middleware-NoCaptureErrorResponse', - 'Decorator', - 'DecoratorWithAsyncHandler', - 'Decorator-Disabled', - 'Decorator-NoCaptureErrorResponse', - ]; - invocationsMap = await addLambdaFunctions(stack, functionConfigs, table); - - await deployStack(integTestApp, stack); - - // Act - Object.values(invocationsMap).forEach(async ({ functionName }) => { - for (let i = 0; i < invocations; i++) { - await lambdaClient.invoke({ - FunctionName: functionName, - LogType: 'Tail', - Payload: JSON.stringify({ - throw: i === invocations - 1 ? true : false, // only last invocation should throw - sdkV2: i === 1 ? 'all' : 'client', // only second invocation should use captureAll - invocation: i + 1, // Pass invocation number for easier debugging - }), - }).promise(); - } - }); +// beforeAll(async () => { + +// // Prepare +// integTestApp = new App(); +// stack = new Stack(integTestApp, 'TracerIntegTest'); // TODO: change stack name to be unique + +// const table = new Table(stack, 'Table', { +// tableName: randomUUID(), +// partitionKey: { +// name: 'id', +// type: AttributeType.STRING +// }, +// billingMode: BillingMode.PAY_PER_REQUEST, +// removalPolicy: RemovalPolicy.DESTROY +// }); + +// const functionConfigs = [ +// 'Manual', +// 'Middleware', +// 'Middleware-Disabled', +// 'Middleware-NoCaptureErrorResponse', +// 'Decorator', +// 'DecoratorWithAsyncHandler', +// 'Decorator-Disabled', +// 'Decorator-NoCaptureErrorResponse', +// ]; +// invocationsMap = await addLambdaFunctions(stack, functionConfigs, table); + +// await deployStack(integTestApp, stack); + +// // Act +// Object.values(invocationsMap).forEach(async ({ functionName }) => { +// for (let i = 0; i < invocations; i++) { +// await lambdaClient.invoke({ +// FunctionName: functionName, +// LogType: 'Tail', +// Payload: JSON.stringify({ +// throw: i === invocations - 1 ? true : false, // only last invocation should throw +// sdkV2: i === 1 ? 'all' : 'client', // only second invocation should use captureAll +// invocation: i + 1, // Pass invocation number for easier debugging +// }), +// }).promise(); +// } +// }); - }, SETUP_TIMEOUT); +// }, SETUP_TIMEOUT); - afterAll(async () => { +// afterAll(async () => { - if (!process.env.DISABLE_TEARDOWN) { - await destroyStack(integTestApp, stack); - } +// if (!process.env.DISABLE_TEARDOWN) { +// await destroyStack(integTestApp, stack); +// } - }, TEARDOWN_TIMEOUT); +// }, TEARDOWN_TIMEOUT); - it('Verifies that a when Tracer is used to manually instrument a function all custom traces are generated with correct annotations and metadata', async () => { +// it('Verifies that a when Tracer is used to manually instrument a function all custom traces are generated with correct annotations and metadata', async () => { - const resourceArn = invocationsMap['Manual'].resourceArn; - const expectedServiceName = invocationsMap['Manual'].serviceName; +// const resourceArn = invocationsMap['Manual'].resourceArn; +// const expectedServiceName = invocationsMap['Manual'].serviceName; - // Assess - // Retrieve traces from X-Ray using Resource ARN as filter - const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // Assess +// // Retrieve traces from X-Ray using Resource ARN as filter +// const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - for (let i = 0; i < invocations; i++) { - expect(sortedTraces[i].Segments.length).toBe(5); +// for (let i = 0; i < invocations; i++) { +// expect(sortedTraces[i].Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); +// if (invocationSubsegment?.subsegments !== undefined) { +// expect(invocationSubsegment?.subsegments?.length).toBe(1); - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); +// const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// expect(handlerSubsegment.name).toBe('## index.handler'); - if (handlerSubsegment?.subsegments !== undefined) { - expect(handlerSubsegment?.subsegments?.length).toBe(3); - - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); +// if (handlerSubsegment?.subsegments !== undefined) { +// expect(handlerSubsegment?.subsegments?.length).toBe(3); + +// const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); +// // Assert that there are exactly two subsegment with the name 'DynamoDB' +// expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // Assert that there is exactly one subsegment with the name 'httpbin.org' +// expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // Assert that there are exactly zero other subsegments +// expect(subsegments.get('other')?.length).toBe(0); - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); +// const { annotations, metadata } = handlerSubsegment; + +// if (annotations !== undefined && metadata !== undefined) { +// // Assert that the annotations are as expected +// expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// expect(annotations['Service']).toEqual(expectedServiceName); +// expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // Assert that the metadata object is as expected +// expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// .toEqual(expectedCustomMetadataValue); - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.fault).toBe(true); - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - } else { - // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) - .toEqual(expectedCustomResponseValue); - } - } else { - // Make test fail if there are no annotations or metadata - expect('annotations !== undefined && metadata !== undefined') - .toBe('annotations === undefined && metadata === undefined'); - } - } else { - // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - expect('handlerSubsegment?.subsegments !== undefined') - .toBe('handlerSubsegment?.subsegments === undefined'); - } - } else { - // Make test fail if the Invocation subsegment doesn't have an handler subsebment - expect('invocationSubsegment?.subsegments !== undefined') - .toBe('invocationSubsegment?.subsegments === undefined'); - } - } - - }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when Tracer is used as middleware all custom traces are generated with correct annotations and metadata', async () => { +// if (i === invocations - 1) { +// // Assert that the subsegment has the expected fault +// expect(invocationSubsegment.error).toBe(true); +// expect(handlerSubsegment.fault).toBe(true); +// expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); +// expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); +// } else { +// // Assert that the metadata object contains the response +// expect(metadata[expectedServiceName]['index.handler response']) +// .toEqual(expectedCustomResponseValue); +// } +// } else { +// // Make test fail if there are no annotations or metadata +// expect('annotations !== undefined && metadata !== undefined') +// .toBe('annotations === undefined && metadata === undefined'); +// } +// } else { +// // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// expect('handlerSubsegment?.subsegments !== undefined') +// .toBe('handlerSubsegment?.subsegments === undefined'); +// } +// } else { +// // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// expect('invocationSubsegment?.subsegments !== undefined') +// .toBe('invocationSubsegment?.subsegments === undefined'); +// } +// } + +// }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when Tracer is used as middleware all custom traces are generated with correct annotations and metadata', async () => { - // const resourceArn = invocationsMap['Middleware'].resourceArn; - // const expectedServiceName = invocationsMap['Middleware'].serviceName; +// // const resourceArn = invocationsMap['Middleware'].resourceArn; +// // const expectedServiceName = invocationsMap['Middleware'].serviceName; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(5); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(5); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // if (invocationSubsegment?.subsegments !== undefined) { - // expect(invocationSubsegment?.subsegments?.length).toBe(1); +// // if (invocationSubsegment?.subsegments !== undefined) { +// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - // const handlerSubsegment = invocationSubsegment?.subsegments[0]; - // expect(handlerSubsegment.name).toBe('## index.handler'); +// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// // expect(handlerSubsegment.name).toBe('## index.handler'); - // if (handlerSubsegment?.subsegments !== undefined) { - // expect(handlerSubsegment?.subsegments?.length).toBe(3); - - // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // // Assert that there are exactly two subsegment with the name 'DynamoDB' - // expect(subsegments.get('DynamoDB')?.length).toBe(2); - // // Assert that there is exactly one subsegment with the name 'httpbin.org' - // expect(subsegments.get('httpbin.org')?.length).toBe(1); - // // Assert that there are exactly zero other subsegments - // expect(subsegments.get('other')?.length).toBe(0); +// // if (handlerSubsegment?.subsegments !== undefined) { +// // expect(handlerSubsegment?.subsegments?.length).toBe(3); + +// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); +// // // Assert that there are exactly two subsegment with the name 'DynamoDB' +// // expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // // Assert that there is exactly one subsegment with the name 'httpbin.org' +// // expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // // Assert that there are exactly zero other subsegments +// // expect(subsegments.get('other')?.length).toBe(0); - // const { annotations, metadata } = handlerSubsegment; - - // if (annotations !== undefined && metadata !== undefined) { - // // Assert that the annotations are as expected - // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - // expect(annotations['Service']).toEqual(expectedServiceName); - // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - // .toEqual(expectedCustomMetadataValue); +// // const { annotations, metadata } = handlerSubsegment; + +// // if (annotations !== undefined && metadata !== undefined) { +// // // Assert that the annotations are as expected +// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// // expect(annotations['Service']).toEqual(expectedServiceName); +// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// // .toEqual(expectedCustomMetadataValue); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // expect(handlerSubsegment.fault).toBe(true); - // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - // } else { - // // Assert that the metadata object contains the response - // expect(metadata[expectedServiceName]['index.handler response']) - // .toEqual(expectedCustomResponseValue); - // } - // } else { - // // Make test fail if there are no annotations or metadata - // expect('annotations !== undefined && metadata !== undefined') - // .toBe('annotations === undefined && metadata === undefined'); - // } - // } else { - // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - // expect('handlerSubsegment?.subsegments !== undefined') - // .toBe('handlerSubsegment?.subsegments === undefined'); - // } - // } else { - // // Make test fail if the Invocation subsegment doesn't have an handler subsebment - // expect('invocationSubsegment?.subsegments !== undefined') - // .toBe('invocationSubsegment?.subsegments === undefined'); - // } - // } - - // }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when Tracer is used as middleware, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // expect(handlerSubsegment.fault).toBe(true); +// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); +// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); +// // } else { +// // // Assert that the metadata object contains the response +// // expect(metadata[expectedServiceName]['index.handler response']) +// // .toEqual(expectedCustomResponseValue); +// // } +// // } else { +// // // Make test fail if there are no annotations or metadata +// // expect('annotations !== undefined && metadata !== undefined') +// // .toBe('annotations === undefined && metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// // expect('handlerSubsegment?.subsegments !== undefined') +// // .toBe('handlerSubsegment?.subsegments === undefined'); +// // } +// // } else { +// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// // expect('invocationSubsegment?.subsegments !== undefined') +// // .toBe('invocationSubsegment?.subsegments === undefined'); +// // } +// // } + +// // }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when Tracer is used as middleware, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - // const resourceArn = invocationsMap['Middleware-NoCaptureErrorResponse'].resourceArn; - // const expectedServiceName = invocationsMap['Middleware-NoCaptureErrorResponse'].serviceName; +// // const resourceArn = invocationsMap['Middleware-NoCaptureErrorResponse'].resourceArn; +// // const expectedServiceName = invocationsMap['Middleware-NoCaptureErrorResponse'].serviceName; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(5); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(5); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // if (invocationSubsegment?.subsegments !== undefined) { - // expect(invocationSubsegment?.subsegments?.length).toBe(1); +// // if (invocationSubsegment?.subsegments !== undefined) { +// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - // const handlerSubsegment = invocationSubsegment?.subsegments[0]; - // expect(handlerSubsegment.name).toBe('## index.handler'); +// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// // expect(handlerSubsegment.name).toBe('## index.handler'); - // if (handlerSubsegment?.subsegments !== undefined) { - // expect(handlerSubsegment?.subsegments?.length).toBe(3); - - // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // // Assert that there are exactly two subsegment with the name 'DynamoDB' - // expect(subsegments.get('DynamoDB')?.length).toBe(2); - // // Assert that there is exactly one subsegment with the name 'httpbin.org' - // expect(subsegments.get('httpbin.org')?.length).toBe(1); - // // Assert that there are exactly zero other subsegments - // expect(subsegments.get('other')?.length).toBe(0); +// // if (handlerSubsegment?.subsegments !== undefined) { +// // expect(handlerSubsegment?.subsegments?.length).toBe(3); + +// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); +// // // Assert that there are exactly two subsegment with the name 'DynamoDB' +// // expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // // Assert that there is exactly one subsegment with the name 'httpbin.org' +// // expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // // Assert that there are exactly zero other subsegments +// // expect(subsegments.get('other')?.length).toBe(0); - // const { annotations, metadata } = handlerSubsegment; - - // if (annotations !== undefined && metadata !== undefined) { - // // Assert that the annotations are as expected - // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - // expect(annotations['Service']).toEqual(expectedServiceName); - // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - // .toEqual(expectedCustomMetadataValue); +// // const { annotations, metadata } = handlerSubsegment; + +// // if (annotations !== undefined && metadata !== undefined) { +// // // Assert that the annotations are as expected +// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// // expect(annotations['Service']).toEqual(expectedServiceName); +// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// // .toEqual(expectedCustomMetadataValue); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // expect(handlerSubsegment.error).toBe(true); - // // Assert that no error was captured on the subsegment - // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); - // } else { - // // Assert that the metadata object does not contain the response object - // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); - // } - // } else { - // // Make test fail if there are no annotations or metadata - // expect('annotations !== undefined && metadata !== undefined') - // .toBe('annotations === undefined && metadata === undefined'); - // } - // } else { - // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - // expect('handlerSubsegment?.subsegments !== undefined') - // .toBe('handlerSubsegment?.subsegments === undefined'); - // } - // } else { - // // Make test fail if the Invocation subsegment doesn't have an handler subsebment - // expect('invocationSubsegment?.subsegments !== undefined') - // .toBe('invocationSubsegment?.subsegments === undefined'); - // } - // } - - // }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when tracing is disabled in middleware mode no custom traces are generated', async () => { +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // expect(handlerSubsegment.error).toBe(true); +// // // Assert that no error was captured on the subsegment +// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); +// // } else { +// // // Assert that the metadata object does not contain the response object +// // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); +// // } +// // } else { +// // // Make test fail if there are no annotations or metadata +// // expect('annotations !== undefined && metadata !== undefined') +// // .toBe('annotations === undefined && metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// // expect('handlerSubsegment?.subsegments !== undefined') +// // .toBe('handlerSubsegment?.subsegments === undefined'); +// // } +// // } else { +// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// // expect('invocationSubsegment?.subsegments !== undefined') +// // .toBe('invocationSubsegment?.subsegments === undefined'); +// // } +// // } + +// // }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when tracing is disabled in middleware mode no custom traces are generated', async () => { - // const resourceArn = invocationsMap['Middleware-Disabled'].resourceArn; +// // const resourceArn = invocationsMap['Middleware-Disabled'].resourceArn; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(2); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(2); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // expect(invocationSubsegment?.subsegments).toBeUndefined(); +// // expect(invocationSubsegment?.subsegments).toBeUndefined(); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // } - // } +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // } +// // } - // }, TEST_CASE_TIMEOUT); +// // }, TEST_CASE_TIMEOUT); - // it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { +// // it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { - // const resourceArn = invocationsMap['Decorator'].resourceArn; - // const expectedServiceName = invocationsMap['Decorator'].serviceName; +// // const resourceArn = invocationsMap['Decorator'].resourceArn; +// // const expectedServiceName = invocationsMap['Decorator'].serviceName; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(5); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(5); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // if (invocationSubsegment?.subsegments !== undefined) { - // expect(invocationSubsegment?.subsegments?.length).toBe(1); +// // if (invocationSubsegment?.subsegments !== undefined) { +// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - // const handlerSubsegment = invocationSubsegment?.subsegments[0]; - // expect(handlerSubsegment.name).toBe('## index.handler'); +// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// // expect(handlerSubsegment.name).toBe('## index.handler'); - // if (handlerSubsegment?.subsegments !== undefined) { - // expect(handlerSubsegment?.subsegments?.length).toBe(4); +// // if (handlerSubsegment?.subsegments !== undefined) { +// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // // Assert that there are exactly two subsegment with the name 'DynamoDB' - // expect(subsegments.get('DynamoDB')?.length).toBe(2); - // // Assert that there is exactly one subsegment with the name 'httpbin.org' - // expect(subsegments.get('httpbin.org')?.length).toBe(1); - // // Assert that there is exactly one subsegment with the name '### myMethod' - // expect(subsegments.get('### myMethod')?.length).toBe(1); - // // Assert that there are exactly zero other subsegments - // expect(subsegments.get('other')?.length).toBe(0); - - // const methodSubsegment = subsegments.get('### myMethod') || []; - // const { metadata } = methodSubsegment[0]; - - // if (metadata !== undefined) { - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName]['myMethod response']) - // .toEqual(expectedCustomResponseValue); - // } else { - // // Make test fail if there is no metadata - // expect('metadata !== undefined') - // .toBe('metadata === undefined'); - // } - // } else { - // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - // expect('handlerSubsegment?.subsegments !== undefined') - // .toBe('handlerSubsegment?.subsegments === undefined'); - // } +// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); +// // // Assert that there are exactly two subsegment with the name 'DynamoDB' +// // expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // // Assert that there is exactly one subsegment with the name 'httpbin.org' +// // expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // // Assert that there is exactly one subsegment with the name '### myMethod' +// // expect(subsegments.get('### myMethod')?.length).toBe(1); +// // // Assert that there are exactly zero other subsegments +// // expect(subsegments.get('other')?.length).toBe(0); + +// // const methodSubsegment = subsegments.get('### myMethod') || []; +// // const { metadata } = methodSubsegment[0]; + +// // if (metadata !== undefined) { +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName]['myMethod response']) +// // .toEqual(expectedCustomResponseValue); +// // } else { +// // // Make test fail if there is no metadata +// // expect('metadata !== undefined') +// // .toBe('metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// // expect('handlerSubsegment?.subsegments !== undefined') +// // .toBe('handlerSubsegment?.subsegments === undefined'); +// // } - // const { annotations, metadata } = handlerSubsegment; - - // if (annotations !== undefined && metadata !== undefined) { - // // Assert that the annotations are as expected - // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - // expect(annotations['Service']).toEqual(expectedServiceName); - // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - // .toEqual(expectedCustomMetadataValue); +// // const { annotations, metadata } = handlerSubsegment; + +// // if (annotations !== undefined && metadata !== undefined) { +// // // Assert that the annotations are as expected +// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// // expect(annotations['Service']).toEqual(expectedServiceName); +// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// // .toEqual(expectedCustomMetadataValue); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // expect(handlerSubsegment.fault).toBe(true); - // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - // } else { - // // Assert that the metadata object contains the response - // expect(metadata[expectedServiceName]['index.handler response']) - // .toEqual(expectedCustomResponseValue); - // } - // } else { - // // Make test fail if there are no annotations or metadata - // expect('annotations !== undefined && metadata !== undefined') - // .toBe('annotations === undefined && metadata === undefined'); - // } - // } else { - // // Make test fail if the Invocation subsegment doesn't have an handler subsebment - // expect('invocationSubsegment?.subsegments !== undefined') - // .toBe('invocationSubsegment?.subsegments === undefined'); - // } - // } - - // }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when Tracer is used as decorator on an async handler all custom traces are generated with correct annotations and metadata', async () => { +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // expect(handlerSubsegment.fault).toBe(true); +// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); +// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); +// // } else { +// // // Assert that the metadata object contains the response +// // expect(metadata[expectedServiceName]['index.handler response']) +// // .toEqual(expectedCustomResponseValue); +// // } +// // } else { +// // // Make test fail if there are no annotations or metadata +// // expect('annotations !== undefined && metadata !== undefined') +// // .toBe('annotations === undefined && metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// // expect('invocationSubsegment?.subsegments !== undefined') +// // .toBe('invocationSubsegment?.subsegments === undefined'); +// // } +// // } + +// // }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when Tracer is used as decorator on an async handler all custom traces are generated with correct annotations and metadata', async () => { - // const resourceArn = invocationsMap['DecoratorWithAsyncHandler'].resourceArn; - // const expectedServiceName = invocationsMap['DecoratorWithAsyncHandler'].serviceName; +// // const resourceArn = invocationsMap['DecoratorWithAsyncHandler'].resourceArn; +// // const expectedServiceName = invocationsMap['DecoratorWithAsyncHandler'].serviceName; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(5); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(5); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // if (invocationSubsegment?.subsegments !== undefined) { - // expect(invocationSubsegment?.subsegments?.length).toBe(1); +// // if (invocationSubsegment?.subsegments !== undefined) { +// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - // const handlerSubsegment = invocationSubsegment?.subsegments[0]; - // expect(handlerSubsegment.name).toBe('## index.handler'); +// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// // expect(handlerSubsegment.name).toBe('## index.handler'); - // if (handlerSubsegment?.subsegments !== undefined) { - // expect(handlerSubsegment?.subsegments?.length).toBe(4); +// // if (handlerSubsegment?.subsegments !== undefined) { +// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // // Assert that there are exactly two subsegment with the name 'DynamoDB' - // expect(subsegments.get('DynamoDB')?.length).toBe(2); - // // Assert that there is exactly one subsegment with the name 'httpbin.org' - // expect(subsegments.get('httpbin.org')?.length).toBe(1); - // // Assert that there is exactly one subsegment with the name '### myMethod' - // expect(subsegments.get('### myMethod')?.length).toBe(1); - // // Assert that there are exactly zero other subsegments - // expect(subsegments.get('other')?.length).toBe(0); - - // const methodSubsegment = subsegments.get('### myMethod') || []; - // const { metadata } = methodSubsegment[0]; - - // if (metadata !== undefined) { - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName]['myMethod response']) - // .toEqual(expectedCustomResponseValue); - // } else { - // // Make test fail if there is no metadata - // expect('metadata !== undefined') - // .toBe('metadata === undefined'); - // } - // } else { - // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - // expect('handlerSubsegment?.subsegments !== undefined') - // .toBe('handlerSubsegment?.subsegments === undefined'); - // } +// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); +// // // Assert that there are exactly two subsegment with the name 'DynamoDB' +// // expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // // Assert that there is exactly one subsegment with the name 'httpbin.org' +// // expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // // Assert that there is exactly one subsegment with the name '### myMethod' +// // expect(subsegments.get('### myMethod')?.length).toBe(1); +// // // Assert that there are exactly zero other subsegments +// // expect(subsegments.get('other')?.length).toBe(0); + +// // const methodSubsegment = subsegments.get('### myMethod') || []; +// // const { metadata } = methodSubsegment[0]; + +// // if (metadata !== undefined) { +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName]['myMethod response']) +// // .toEqual(expectedCustomResponseValue); +// // } else { +// // // Make test fail if there is no metadata +// // expect('metadata !== undefined') +// // .toBe('metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// // expect('handlerSubsegment?.subsegments !== undefined') +// // .toBe('handlerSubsegment?.subsegments === undefined'); +// // } - // const { annotations, metadata } = handlerSubsegment; - - // if (annotations !== undefined && metadata !== undefined) { - // // Assert that the annotations are as expected - // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - // expect(annotations['Service']).toEqual(expectedServiceName); - // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - // .toEqual(expectedCustomMetadataValue); +// // const { annotations, metadata } = handlerSubsegment; + +// // if (annotations !== undefined && metadata !== undefined) { +// // // Assert that the annotations are as expected +// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// // expect(annotations['Service']).toEqual(expectedServiceName); +// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// // .toEqual(expectedCustomMetadataValue); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // expect(handlerSubsegment.fault).toBe(true); - // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - // } else { - // // Assert that the metadata object contains the response - // expect(metadata[expectedServiceName]['index.handler response']) - // .toEqual(expectedCustomResponseValue); - // } - // } else { - // // Make test fail if there are no annotations or metadata - // expect('annotations !== undefined && metadata !== undefined') - // .toBe('annotations === undefined && metadata === undefined'); - // } - // } else { - // // Make test fail if the Invocation subsegment doesn't have an handler subsebment - // expect('invocationSubsegment?.subsegments !== undefined') - // .toBe('invocationSubsegment?.subsegments === undefined'); - // } - // } - - // }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when Tracer is used as decorator, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // expect(handlerSubsegment.fault).toBe(true); +// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); +// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); +// // } else { +// // // Assert that the metadata object contains the response +// // expect(metadata[expectedServiceName]['index.handler response']) +// // .toEqual(expectedCustomResponseValue); +// // } +// // } else { +// // // Make test fail if there are no annotations or metadata +// // expect('annotations !== undefined && metadata !== undefined') +// // .toBe('annotations === undefined && metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// // expect('invocationSubsegment?.subsegments !== undefined') +// // .toBe('invocationSubsegment?.subsegments === undefined'); +// // } +// // } + +// // }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when Tracer is used as decorator, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - // const resourceArn = invocationsMap['Decorator-NoCaptureErrorResponse'].resourceArn; - // const expectedServiceName = invocationsMap['Decorator-NoCaptureErrorResponse'].serviceName; +// // const resourceArn = invocationsMap['Decorator-NoCaptureErrorResponse'].resourceArn; +// // const expectedServiceName = invocationsMap['Decorator-NoCaptureErrorResponse'].serviceName; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(5); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(5); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // if (invocationSubsegment?.subsegments !== undefined) { - // expect(invocationSubsegment?.subsegments?.length).toBe(1); +// // if (invocationSubsegment?.subsegments !== undefined) { +// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - // const handlerSubsegment = invocationSubsegment?.subsegments[0]; - // expect(handlerSubsegment.name).toBe('## index.handler'); +// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; +// // expect(handlerSubsegment.name).toBe('## index.handler'); - // if (handlerSubsegment?.subsegments !== undefined) { - // expect(handlerSubsegment?.subsegments?.length).toBe(4); +// // if (handlerSubsegment?.subsegments !== undefined) { +// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); - // // Assert that there are exactly two subsegment with the name 'DynamoDB' - // expect(subsegments.get('DynamoDB')?.length).toBe(2); - // // Assert that there is exactly one subsegment with the name 'httpbin.org' - // expect(subsegments.get('httpbin.org')?.length).toBe(1); - // // Assert that there is exactly one subsegment with the name '### myMethod' - // expect(subsegments.get('### myMethod')?.length).toBe(1); - // // Assert that there are exactly zero other subsegments - // expect(subsegments.get('other')?.length).toBe(0); - - // // Assert that no response was captured on the subsegment - // const methodSubsegment = subsegments.get('### myMethod') || []; - // expect(methodSubsegment[0].hasOwnProperty('metadata')).toBe(false); - // } else { - // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment - // expect('handlerSubsegment?.subsegments !== undefined') - // .toBe('handlerSubsegment?.subsegments === undefined'); - // } +// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); +// // // Assert that there are exactly two subsegment with the name 'DynamoDB' +// // expect(subsegments.get('DynamoDB')?.length).toBe(2); +// // // Assert that there is exactly one subsegment with the name 'httpbin.org' +// // expect(subsegments.get('httpbin.org')?.length).toBe(1); +// // // Assert that there is exactly one subsegment with the name '### myMethod' +// // expect(subsegments.get('### myMethod')?.length).toBe(1); +// // // Assert that there are exactly zero other subsegments +// // expect(subsegments.get('other')?.length).toBe(0); + +// // // Assert that no response was captured on the subsegment +// // const methodSubsegment = subsegments.get('### myMethod') || []; +// // expect(methodSubsegment[0].hasOwnProperty('metadata')).toBe(false); +// // } else { +// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment +// // expect('handlerSubsegment?.subsegments !== undefined') +// // .toBe('handlerSubsegment?.subsegments === undefined'); +// // } - // const { annotations, metadata } = handlerSubsegment; - - // if (annotations !== undefined && metadata !== undefined) { - // // Assert that the annotations are as expected - // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - // expect(annotations['Service']).toEqual(expectedServiceName); - // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // // Assert that the metadata object is as expected - // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - // .toEqual(expectedCustomMetadataValue); +// // const { annotations, metadata } = handlerSubsegment; + +// // if (annotations !== undefined && metadata !== undefined) { +// // // Assert that the annotations are as expected +// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); +// // expect(annotations['Service']).toEqual(expectedServiceName); +// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +// // // Assert that the metadata object is as expected +// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) +// // .toEqual(expectedCustomMetadataValue); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // expect(handlerSubsegment.error).toBe(true); - // // Assert that no error was captured on the subsegment - // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); - // } else { - // // Assert that the metadata object does not contain the response object - // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); - // } - // } else { - // // Make test fail if there are no annotations or metadata - // expect('annotations !== undefined && metadata !== undefined') - // .toBe('annotations === undefined && metadata === undefined'); - // } - // } else { - // // Make test fail if the Invocation subsegment doesn't have an handler subsebment - // expect('invocationSubsegment?.subsegments !== undefined') - // .toBe('invocationSubsegment?.subsegments === undefined'); - // } - // } - - // }, TEST_CASE_TIMEOUT); - - // it('Verifies that a when tracing is disabled in decorator mode no custom traces are generated', async () => { +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // expect(handlerSubsegment.error).toBe(true); +// // // Assert that no error was captured on the subsegment +// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); +// // } else { +// // // Assert that the metadata object does not contain the response object +// // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); +// // } +// // } else { +// // // Make test fail if there are no annotations or metadata +// // expect('annotations !== undefined && metadata !== undefined') +// // .toBe('annotations === undefined && metadata === undefined'); +// // } +// // } else { +// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment +// // expect('invocationSubsegment?.subsegments !== undefined') +// // .toBe('invocationSubsegment?.subsegments === undefined'); +// // } +// // } + +// // }, TEST_CASE_TIMEOUT); + +// // it('Verifies that a when tracing is disabled in decorator mode no custom traces are generated', async () => { - // const resourceArn = invocationsMap['Decorator-Disabled'].resourceArn; +// // const resourceArn = invocationsMap['Decorator-Disabled'].resourceArn; - // // Assess - // // Retrieve traces from X-Ray using Resource ARN as filter - // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); +// // // Assess +// // // Retrieve traces from X-Ray using Resource ARN as filter +// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - // for (let i = 0; i < invocations; i++) { - // // Assert that the trace has the expected amount of segments - // expect(sortedTraces[i].Segments.length).toBe(2); +// // for (let i = 0; i < invocations; i++) { +// // // Assert that the trace has the expected amount of segments +// // expect(sortedTraces[i].Segments.length).toBe(2); - // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); +// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - // expect(invocationSubsegment?.subsegments).toBeUndefined(); +// // expect(invocationSubsegment?.subsegments).toBeUndefined(); - // if (i === invocations - 1) { - // // Assert that the subsegment has the expected fault - // expect(invocationSubsegment.error).toBe(true); - // } - // } +// // if (i === invocations - 1) { +// // // Assert that the subsegment has the expected fault +// // expect(invocationSubsegment.error).toBe(true); +// // } +// // } - // }, TEST_CASE_TIMEOUT); +// // }, TEST_CASE_TIMEOUT); -}); \ No newline at end of file +// }); \ No newline at end of file diff --git a/packages/tracing/tests/helpers/tracesUtils.ts b/packages/tracing/tests/helpers/tracesUtils.ts index 403217b2ec..246be9f03e 100644 --- a/packages/tracing/tests/helpers/tracesUtils.ts +++ b/packages/tracing/tests/helpers/tracesUtils.ts @@ -51,7 +51,7 @@ interface ParsedSegment { Id: string } -interface ParsedTrace { +export interface ParsedTrace { Duration: number Id: string LimitExceeded: boolean From 1f5e8ea43a16009e9711c9334c0c3785356ca758 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Tue, 19 Apr 2022 17:34:02 +0200 Subject: [PATCH 04/12] test(tracer): refactor to move reusable code to functions --- packages/commons/tests/utils/e2eUtils.ts | 5 +- .../tests/e2e/allFeatures.manual.test.ts | 60 ++++--------- packages/tracing/tests/helpers/tracesUtils.ts | 89 ++++++++++++++++++- 3 files changed, 109 insertions(+), 45 deletions(-) diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index 805b06f6e8..eafaf3c95c 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -32,6 +32,8 @@ export type StackWithLambdaFunctionOptions = { runtime: string }; +type FunctionPayload = {[key: string]: string | boolean | number}; + export const isValidRuntimeKey = (runtime: string): runtime is TestRuntimesKey => testRuntimeKeys.includes(runtime); export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOptions): Stack => { @@ -57,7 +59,7 @@ export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOpt export const generateUniqueName = (name_prefix: string, uuid: string, runtime: string, testName: string): string => `${name_prefix}-${runtime}-${testName}-${uuid}`.substring(0, 64); -export const invokeFunction = async (functionName: string, times: number = 1, invocationMode: 'PARALLEL' | 'SEQUENTIAL' = 'PARALLEL'): Promise => { +export const invokeFunction = async (functionName: string, times: number = 1, invocationMode: 'PARALLEL' | 'SEQUENTIAL' = 'PARALLEL', payload: FunctionPayload = {}): Promise => { const invocationLogs: InvocationLogs[] = []; const promiseFactory = (): Promise => { @@ -65,6 +67,7 @@ export const invokeFunction = async (functionName: string, times: number = 1, in .invoke({ FunctionName: functionName, LogType: 'Tail', // Wait until execution completes and return all logs + Payload: JSON.stringify(payload), }) .promise() .then((response) => { diff --git a/packages/tracing/tests/e2e/allFeatures.manual.test.ts b/packages/tracing/tests/e2e/allFeatures.manual.test.ts index 18c2aa746b..4814f754b0 100644 --- a/packages/tracing/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.manual.test.ts @@ -10,7 +10,7 @@ import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy, Duration } from 'aws-cdk-lib'; import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; -import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace } from '../helpers/tracesUtils'; +import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction } from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, @@ -43,9 +43,7 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFe const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Manual'); const lambdaFunctionCodeFile = 'allFeatures.manual.test.functionCode.ts'; const expectedServiceName = functionName; -let startTime : Date; -const lambdaClient = new AWS.Lambda(); const xray = new AWS.XRay(); const invocations = 3; let sortedTraces: ParsedTrace[]; @@ -67,36 +65,26 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: beforeAll(async () => { // Prepare - startTime = new Date(); + const startTime = new Date(); const ddbTableName = stackName + '-table'; stack = new Stack(integTestApp, stackName); - const testFunction = new NodejsFunction(stack, functionName, { - entry: path.join(__dirname, lambdaFunctionCodeFile), - functionName: functionName, - handler: 'handler', - tracing: Tracing.ACTIVE, - architecture: Architecture.X86_64, - memorySize: 256, - environment: { - EXPECTED_SERVICE_NAME: expectedServiceName, - EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, - EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, - EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, - EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), - EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), - EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, - POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', - POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', - POWERTOOLS_TRACE_ENABLED: 'true', - TEST_TABLE_NAME: ddbTableName, - }, - timeout: Duration.seconds(30), - bundling: { - externalModules: ['aws-sdk'], - }, - runtime: TEST_RUNTIMES[runtime as TestRuntimesKey], + const entry = path.join(__dirname, lambdaFunctionCodeFile); + const environmentParams = { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + }; + const testFunction = createTracerTestFunction({ + stack, + functionName, + entry, + expectedServiceName, + environmentParams, + runtime }); + const ddbTable = new Table(stack, 'Table', { tableName: ddbTableName, partitionKey: { @@ -112,17 +100,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: await deployStack(integTestApp, stack); // Act - for (let i = 0; i < invocations; i++) { - await lambdaClient.invoke({ - FunctionName: functionName, - LogType: 'Tail', - Payload: JSON.stringify({ - throw: i === invocations - 1 ? true : false, // only last invocation should throw - sdkV2: i === 1 ? 'all' : 'client', // only second invocation should use captureAll - invocation: i + 1, // Pass invocation number for easier debugging - }), - }).promise(); - } + await invokeAllTestCases(functionName); // Retrieve traces from X-Ray for assertion const lambdaFunctionArn = await getFunctionArn(functionName); @@ -145,7 +123,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: const trace = sortedTraces[i]; /** - * Expect the trace to have 5 segments: + * Expect the trace to have 5 segments: * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB (AWS::DynamoDB) diff --git a/packages/tracing/tests/helpers/tracesUtils.ts b/packages/tracing/tests/helpers/tracesUtils.ts index 246be9f03e..b7164f4db5 100644 --- a/packages/tracing/tests/helpers/tracesUtils.ts +++ b/packages/tracing/tests/helpers/tracesUtils.ts @@ -1,6 +1,20 @@ import { XRay } from 'aws-sdk'; import promiseRetry from 'promise-retry'; - +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { + invokeFunction, TestRuntimesKey, TEST_RUNTIMES, +} from '../../../commons/tests/utils/e2eUtils'; +import { Duration, Stack } from 'aws-cdk-lib'; +import path from 'path'; +import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda'; +import { + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, + expectedCustomErrorMessage, +} from '../e2e/constants'; interface ParsedDocument { name: string id: string @@ -58,6 +72,15 @@ export interface ParsedTrace { Segments: ParsedSegment[] } +interface TracerTestFunctionParams { + stack: Stack + functionName: string + entry: string + expectedServiceName: string + environmentParams: { [key: string]: string } + runtime: string +} + const getTraces = async (xrayClient: XRay, startTime: Date, resourceArn: string, expectedTraces: number, expectedSegments: number): Promise => { const retryOptions = { retries: 20, minTimeout: 5_000, maxTimeout: 10_000, factor: 1.25 }; @@ -147,13 +170,73 @@ const splitSegmentsByName = (subsegments: ParsedDocument[], expectedNames: strin return splitSegments; }; +/** + * Invoke function sequentially 3 times with different parameters + * + * invocation: is just a tracking number (it has to start from 1) + * sdkV2: define if we will use `captureAWSClient()` or `captureAWS()` for SDK V2 + * throw: forces the Lambda to throw an error + * + * @param functionName + */ +const invokeAllTestCases = async (functionName: string): Promise => { + await invokeFunction(functionName, 1, 'SEQUENTIAL', { + invocation: 1, + sdkV2: 'client', + throw: false, + }); + await invokeFunction(functionName, 1, 'SEQUENTIAL', { + invocation: 2, + sdkV2: 'all', // only second invocation should use captureAll + throw: false, + }); + await invokeFunction(functionName, 1, 'SEQUENTIAL', { + invocation: 3, + sdkV2: 'client', + throw: true, // only last invocation should throw + }); +}; + +const createTracerTestFunction = (params: TracerTestFunctionParams): NodejsFunction => { + const { stack, functionName, entry, expectedServiceName, environmentParams, runtime } = params; + const func = new NodejsFunction(stack, functionName, { + entry: entry, + functionName: functionName, + handler: 'handler', + tracing: Tracing.ACTIVE, + architecture: Architecture.X86_64, + memorySize: 256, // Default value (128) will take too long to process + environment: { + EXPECTED_SERVICE_NAME: expectedServiceName, + EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, + EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, + EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, + EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), + EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), + EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, + ...environmentParams, + }, + timeout: Duration.seconds(30), // Default value (3 seconds) will time out + bundling: { + // Exclude aws-sdk and use the default one provided by Lambda + externalModules: ['aws-sdk'], + }, + runtime: TEST_RUNTIMES[runtime as TestRuntimesKey], + }); + + return func; +} + export { getTraces, getFunctionSegment, getInvocationSubsegment, - splitSegmentsByName + splitSegmentsByName, + invokeAllTestCases, + createTracerTestFunction, }; export type { ParsedDocument, -}; \ No newline at end of file + TracerTestFunctionParams, +}; From 8e862959cb42ec99d09af3035de2c06e30cc4a1b Mon Sep 17 00:00:00 2001 From: ijemmy Date: Tue, 19 Apr 2022 21:32:14 +0200 Subject: [PATCH 05/12] test(tracer): add middy test + refactor to make it more readable --- .../tests/e2e/allFeatures.manual.test.ts | 134 ++++++------- ...=> allFeatures.middy.test.functionCode.ts} | 0 .../tests/e2e/allFeatures.middy.test.ts | 185 ++++++++++++++++++ .../tracing/tests/helpers/traceAssertions.ts | 34 ++++ packages/tracing/tests/helpers/tracesUtils.ts | 25 ++- 5 files changed, 301 insertions(+), 77 deletions(-) rename packages/tracing/tests/e2e/{tracer.test.Middleware.ts => allFeatures.middy.test.functionCode.ts} (100%) create mode 100644 packages/tracing/tests/e2e/allFeatures.middy.test.ts create mode 100644 packages/tracing/tests/helpers/traceAssertions.ts diff --git a/packages/tracing/tests/e2e/allFeatures.manual.test.ts b/packages/tracing/tests/e2e/allFeatures.manual.test.ts index 4814f754b0..97d014a736 100644 --- a/packages/tracing/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.manual.test.ts @@ -7,15 +7,13 @@ import { randomUUID } from 'crypto'; import path from 'path'; import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Stack, RemovalPolicy, Duration } from 'aws-cdk-lib'; +import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; -import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction } from '../helpers/tracesUtils'; +import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction, getFunctionArn, getFirstSubsegment } from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, - TestRuntimesKey, - TEST_RUNTIMES, } from '../../../commons/tests/utils/e2eUtils'; import { RESOURCE_NAME_PREFIX, @@ -29,8 +27,10 @@ import { expectedCustomResponseValue, expectedCustomErrorMessage, } from './constants'; -import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda'; -import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { + assertErrorAndFault, + assertAnnotation +} from '../helpers/traceAssertions'; const runtime: string = process.env.RUNTIME || 'nodejs14x'; @@ -51,15 +51,6 @@ let sortedTraces: ParsedTrace[]; const integTestApp = new App(); let stack: Stack; -const getFunctionArn = async (functionName: string): Promise => { - const stsClient = new AWS.STS(); - const region = process.env.AWS_REGION; - const identity = await stsClient.getCallerIdentity().promise(); - const account = identity.Account; - - return `arn:aws:lambda:${region}:${account}:function:${functionName}`; -}; - describe(`Tracer E2E tests, all features with manual instantiation for runtime: ${runtime}`, () => { beforeAll(async () => { @@ -114,7 +105,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: } }, TEARDOWN_TIMEOUT); - it('should generate all custom traces with correct annotations and metadata', async () => { + it('should generate all custom traces', async () => { expect(sortedTraces.length).toBe(invocations); @@ -131,70 +122,65 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: * 5. Remote call (httpbin.org) */ expect(trace.Segments.length).toBe(5); - const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 3 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(3); - if (invocationSubsegment?.subsegments !== undefined) { - expect(invocationSubsegment?.subsegments?.length).toBe(1); - - /** - * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) - */ - const handlerSubsegment = invocationSubsegment?.subsegments[0]; - expect(handlerSubsegment.name).toBe('## index.handler'); - - if (handlerSubsegment?.subsegments !== undefined) { - /** - * '## index.handler' subsegment should have 3 subsegments - * 1. DynamoDB (PutItem on the table) - * 2. DynamoDB (PutItem overhead) - * 3. httpbin.org (Remote call) - */ - expect(handlerSubsegment?.subsegments?.length).toBe(3); - - const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); - // Assert that there are exactly two subsegment with the name 'DynamoDB' - expect(subsegments.get('DynamoDB')?.length).toBe(2); - // Assert that there is exactly one subsegment with the name 'httpbin.org' - expect(subsegments.get('httpbin.org')?.length).toBe(1); - // Assert that there are exactly zero other subsegments - expect(subsegments.get('other')?.length).toBe(0); - - const { annotations, metadata } = handlerSubsegment; - - if (annotations !== undefined && metadata !== undefined) { - // Assert that the annotations are as expected - expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); - expect(annotations['Service']).toEqual(expectedServiceName); - expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); - // Assert that the metadata object is as expected - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) - .toEqual(expectedCustomMetadataValue); - - if (i === invocations - 1) { - // Assert that the subsegment has the expected fault - expect(invocationSubsegment.error).toBe(true); - expect(handlerSubsegment.fault).toBe(true); - expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); - expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); - } else { - // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) - .toEqual(expectedCustomResponseValue); - } - } else { - // Make test fail if there are no annotations or metadata - fail('There is no annotations or metadata in the handler subsgement') - } - } else { - fail('Handler subsegment does NOT have any subsebments'); - } - } else { - fail('Invocation subsegment does NOT have a handler subsebment'); + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + assertErrorAndFault(invocationSubsegment, expectedCustomErrorMessage); } } }, TEST_CASE_TIMEOUT); + + it('should have correct annotations and metadata', async () => { + for (let i = 0; i < invocations; i++) { + const trace = sortedTraces[i]; + const invocationSubsegment = getInvocationSubsegment(trace); + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + const { annotations, metadata } = handlerSubsegment; + + const isColdStart = (i === 0); + assertAnnotation({ + annotations, + isColdStart, + expectedServiceName, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + }); + + if (!metadata) { + fail('metadata is missing'); + } + expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + .toEqual(expectedCustomMetadataValue); + + const shouldThrowAnError = (i === (invocations - 1)); + if (!shouldThrowAnError) { + // Assert that the metadata object contains the response + expect(metadata[expectedServiceName]['index.handler response']) + .toEqual(expectedCustomResponseValue); + } + } + }, TEST_CASE_TIMEOUT); }); diff --git a/packages/tracing/tests/e2e/tracer.test.Middleware.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.functionCode.ts similarity index 100% rename from packages/tracing/tests/e2e/tracer.test.Middleware.ts rename to packages/tracing/tests/e2e/allFeatures.middy.test.functionCode.ts diff --git a/packages/tracing/tests/e2e/allFeatures.middy.test.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.ts new file mode 100644 index 0000000000..388998029b --- /dev/null +++ b/packages/tracing/tests/e2e/allFeatures.middy.test.ts @@ -0,0 +1,185 @@ +/** + * Test tracer manual mode + * + * @group e2e/tracer/middy + */ + +import { randomUUID } from 'crypto'; +import path from 'path'; +import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; +import * as AWS from 'aws-sdk'; +import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction, getFunctionArn, ParsedDocument, getFirstSubsegment } from '../helpers/tracesUtils'; +import { + generateUniqueName, + isValidRuntimeKey, +} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, + expectedCustomErrorMessage, +} from './constants'; +import { + assertAnnotation, + assertErrorAndFault, +} from '../helpers/traceAssertions'; + +const runtime: string = process.env.RUNTIME || 'nodejs14x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const uuid = randomUUID(); +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Middy'); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Middy'); +const lambdaFunctionCodeFile = 'allFeatures.middy.test.functionCode.ts'; +const expectedServiceName = functionName; + +const xray = new AWS.XRay(); +const invocations = 3; +let sortedTraces: ParsedTrace[]; + +const integTestApp = new App(); +let stack: Stack; + +describe(`Tracer E2E tests, all features with middy instantiation for runtime: ${runtime}`, () => { + + beforeAll(async () => { + + // Prepare + const startTime = new Date(); + const ddbTableName = stackName + '-table'; + stack = new Stack(integTestApp, stackName); + + const entry = path.join(__dirname, lambdaFunctionCodeFile); + const environmentParams = { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + }; + const testFunction = createTracerTestFunction({ + stack, + functionName, + entry, + expectedServiceName, + environmentParams, + runtime + }); + + const ddbTable = new Table(stack, 'Table', { + tableName: ddbTableName, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY + }); + + ddbTable.grantWriteData(testFunction); + + await deployStack(integTestApp, stack); + + // Act + await invokeAllTestCases(functionName); + + // Retrieve traces from X-Ray for assertion + const lambdaFunctionArn = await getFunctionArn(functionName); + sortedTraces = await getTraces(xray, startTime, lambdaFunctionArn, invocations, 5); + + }, SETUP_TIMEOUT); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + + it('should generate all custom traces', async () => { + + expect(sortedTraces.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = sortedTraces[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 3 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(3); + + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + assertErrorAndFault(invocationSubsegment, expectedCustomErrorMessage); + } + } + + }, TEST_CASE_TIMEOUT); + + it('should have correct annotations and metadata', async () => { + for (let i = 0; i < invocations; i++) { + const trace = sortedTraces[i]; + const invocationSubsegment = getInvocationSubsegment(trace); + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + const { annotations, metadata } = handlerSubsegment; + + const isColdStart = (i === 0); + assertAnnotation({ + annotations, + isColdStart, + expectedServiceName, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + }); + + if (!metadata) { + fail('metadata is missing'); + } + expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + .toEqual(expectedCustomMetadataValue); + + const shouldThrowAnError = (i === (invocations - 1)); + if (!shouldThrowAnError) { + // Assert that the metadata object contains the response + expect(metadata[expectedServiceName]['index.handler response']) + .toEqual(expectedCustomResponseValue); + } + } + }, TEST_CASE_TIMEOUT); +}); + diff --git a/packages/tracing/tests/helpers/traceAssertions.ts b/packages/tracing/tests/helpers/traceAssertions.ts new file mode 100644 index 0000000000..8571455f15 --- /dev/null +++ b/packages/tracing/tests/helpers/traceAssertions.ts @@ -0,0 +1,34 @@ +import { getFirstSubsegment, ParsedDocument } from './tracesUtils'; + +export interface AssertAnnotationParams { + annotations: ParsedDocument['annotations'] + isColdStart: boolean + expectedServiceName: string + expectedCustomAnnotationKey: string + expectedCustomAnnotationValue: string | number | boolean +} +export const assertAnnotation = (params: AssertAnnotationParams): void => { + const { + annotations, + isColdStart, + expectedServiceName, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue + } = params; + + if (!annotations) { + fail('annotation is missing'); + } + expect(annotations['ColdStart']).toEqual(isColdStart); + expect(annotations['Service']).toEqual(expectedServiceName); + expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); +}; + +export const assertErrorAndFault = (invocationSubsegment: ParsedDocument, expectedCustomErrorMessage: string): void => { + expect(invocationSubsegment.error).toBe(true); + + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.fault).toBe(true); + expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); + expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); +}; \ No newline at end of file diff --git a/packages/tracing/tests/helpers/tracesUtils.ts b/packages/tracing/tests/helpers/tracesUtils.ts index b7164f4db5..861cb25451 100644 --- a/packages/tracing/tests/helpers/tracesUtils.ts +++ b/packages/tracing/tests/helpers/tracesUtils.ts @@ -1,11 +1,10 @@ -import { XRay } from 'aws-sdk'; +import AWS, { XRay } from 'aws-sdk'; import promiseRetry from 'promise-retry'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { invokeFunction, TestRuntimesKey, TEST_RUNTIMES, } from '../../../commons/tests/utils/e2eUtils'; import { Duration, Stack } from 'aws-cdk-lib'; -import path from 'path'; import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda'; import { expectedCustomAnnotationKey, @@ -146,6 +145,15 @@ const getFunctionSegment = (trace: ParsedTrace): ParsedSegment => { return functionSegment; }; +const getFirstSubsegment = (segment: ParsedDocument): ParsedDocument => { + const subsegments = segment.subsegments; + if (!subsegments || subsegments.length == 0) { + throw new Error('segment should have subsegments'); + } + + return subsegments[0]; +}; + const getInvocationSubsegment = (trace: ParsedTrace): ParsedDocument => { const functionSegment = getFunctionSegment(trace); const invocationSubsegment = functionSegment.Document?.subsegments @@ -225,15 +233,26 @@ const createTracerTestFunction = (params: TracerTestFunctionParams): NodejsFunct }); return func; -} +}; + +const getFunctionArn = async (functionName: string): Promise => { + const stsClient = new AWS.STS(); + const region = process.env.AWS_REGION; + const identity = await stsClient.getCallerIdentity().promise(); + const account = identity.Account; + + return `arn:aws:lambda:${region}:${account}:function:${functionName}`; +}; export { getTraces, getFunctionSegment, + getFirstSubsegment, getInvocationSubsegment, splitSegmentsByName, invokeAllTestCases, createTracerTestFunction, + getFunctionArn, }; export type { From db3ee54015e49a7bb7684f0ad4d6988781667709 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 16:03:30 +0200 Subject: [PATCH 06/12] test(tracer): Add two variant of middy case --- .../tests/e2e/allFeatures.middy.test.ts | 213 +++++++++++++--- packages/tracing/tests/e2e/constants.ts | 2 +- packages/tracing/tests/e2e/tracer.test.ts | 241 ------------------ packages/tracing/tests/helpers/tracesUtils.ts | 9 +- 4 files changed, 185 insertions(+), 280 deletions(-) diff --git a/packages/tracing/tests/e2e/allFeatures.middy.test.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.ts index 388998029b..ab8735276e 100644 --- a/packages/tracing/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.middy.test.ts @@ -10,7 +10,15 @@ import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; -import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction, getFunctionArn, ParsedDocument, getFirstSubsegment } from '../helpers/tracesUtils'; +import { + getTraces, + getInvocationSubsegment, + splitSegmentsByName, + invokeAllTestCases, + createTracerTestFunction, + getFunctionArn, + getFirstSubsegment, +} from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, @@ -38,15 +46,38 @@ if (!isValidRuntimeKey(runtime)) { throw new Error(`Invalid runtime key value: ${runtime}`); } -const uuid = randomUUID(); -const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Middy'); -const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Middy'); +/** + * We will create a stack with 3 Lambda functions: + * 1. With all flags enabled (capture both response and error) + * 2. Do not capture error or response + * 3. Do not enable tracer + */ +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, randomUUID(), runtime, 'AllFeatures-Middy'); const lambdaFunctionCodeFile = 'allFeatures.middy.test.functionCode.ts'; -const expectedServiceName = functionName; +let startTime: Date; + +/** + * Function #1 is with all flags enabled. + */ +const uuidFunction1 = randomUUID(); +const functionNameWithAllFlagsEnabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction1, runtime, 'AllFeatures-Middy-AllFlagsEnabled'); +const serviceNameWithAllFlagsEnabled = functionNameWithAllFlagsEnabled; + +/** + * Function #2 doesn't capture error or response + */ +const uuidFunction2 = randomUUID(); +const functionNameWithNoCaptureErrorOrResponse = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction2, runtime, 'AllFeatures-Middy-NoCaptureErrorOrResponse'); +const serviceNameWithNoCaptureErrorOrResponse = functionNameWithNoCaptureErrorOrResponse; +/** + * Function #3 disables tracer + */ +const uuidFunction3 = randomUUID(); +const functionNameWithTracerDisabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction3, runtime, 'AllFeatures-Middy-TracerDisabled'); +const serviceNameWithTracerDisabled = functionNameWithNoCaptureErrorOrResponse; const xray = new AWS.XRay(); const invocations = 3; -let sortedTraces: ParsedTrace[]; const integTestApp = new App(); let stack: Stack; @@ -56,26 +87,13 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ beforeAll(async () => { // Prepare - const startTime = new Date(); + startTime = new Date(); const ddbTableName = stackName + '-table'; stack = new Stack(integTestApp, stackName); - const entry = path.join(__dirname, lambdaFunctionCodeFile); - const environmentParams = { - TEST_TABLE_NAME: ddbTableName, - POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', - POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', - POWERTOOLS_TRACE_ENABLED: 'true', - }; - const testFunction = createTracerTestFunction({ - stack, - functionName, - entry, - expectedServiceName, - environmentParams, - runtime - }); - + /** + * We need a DynamoDB table to connect via SDK so we can trace a PutItem call. + */ const ddbTable = new Table(stack, 'Table', { tableName: ddbTableName, partitionKey: { @@ -86,16 +104,60 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ removalPolicy: RemovalPolicy.DESTROY }); - ddbTable.grantWriteData(testFunction); + const entry = path.join(__dirname, lambdaFunctionCodeFile); + const functionWithAllFlagsEnabled = createTracerTestFunction({ + stack, + functionName: functionNameWithAllFlagsEnabled, + entry, + expectedServiceName: serviceNameWithAllFlagsEnabled, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + }, + runtime + }); + ddbTable.grantWriteData(functionWithAllFlagsEnabled); + + const functionThatDoesNotCapturesErrorAndResponse = createTracerTestFunction({ + stack, + functionName: functionNameWithNoCaptureErrorOrResponse, + entry, + expectedServiceName: serviceNameWithNoCaptureErrorOrResponse, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'false', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'false', + POWERTOOLS_TRACE_ENABLED: 'true', + }, + runtime + }); + ddbTable.grantWriteData(functionThatDoesNotCapturesErrorAndResponse); + + const functionWithTracerDisabled = createTracerTestFunction({ + stack, + functionName: functionNameWithTracerDisabled, + entry, + expectedServiceName: serviceNameWithTracerDisabled, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'false', + }, + runtime + }); + ddbTable.grantWriteData(functionWithTracerDisabled); await deployStack(integTestApp, stack); // Act - await invokeAllTestCases(functionName); - - // Retrieve traces from X-Ray for assertion - const lambdaFunctionArn = await getFunctionArn(functionName); - sortedTraces = await getTraces(xray, startTime, lambdaFunctionArn, invocations, 5); + await Promise.all([ + invokeAllTestCases(functionNameWithAllFlagsEnabled), + invokeAllTestCases(functionNameWithNoCaptureErrorOrResponse), + invokeAllTestCases(functionNameWithTracerDisabled), + ]); }, SETUP_TIMEOUT); @@ -107,11 +169,13 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ it('should generate all custom traces', async () => { - expect(sortedTraces.length).toBe(invocations); + const tracesWhenAllFlagsEnabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithAllFlagsEnabled), invocations, 5); + + expect(tracesWhenAllFlagsEnabled.length).toBe(invocations); // Assess for (let i = 0; i < invocations; i++) { - const trace = sortedTraces[i]; + const trace = tracesWhenAllFlagsEnabled[i]; /** * Expect the trace to have 5 segments: @@ -152,8 +216,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ }, TEST_CASE_TIMEOUT); it('should have correct annotations and metadata', async () => { + const tracesWhenAllFlagsEnabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithAllFlagsEnabled), invocations, 5); + for (let i = 0; i < invocations; i++) { - const trace = sortedTraces[i]; + const trace = tracesWhenAllFlagsEnabled[i]; const invocationSubsegment = getInvocationSubsegment(trace); const handlerSubsegment = getFirstSubsegment(invocationSubsegment); const { annotations, metadata } = handlerSubsegment; @@ -162,7 +228,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ assertAnnotation({ annotations, isColdStart, - expectedServiceName, + expectedServiceName: serviceNameWithAllFlagsEnabled, expectedCustomAnnotationKey, expectedCustomAnnotationValue, }); @@ -170,16 +236,93 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ if (!metadata) { fail('metadata is missing'); } - expect(metadata[expectedServiceName][expectedCustomMetadataKey]) + expect(metadata[serviceNameWithAllFlagsEnabled][expectedCustomMetadataKey]) .toEqual(expectedCustomMetadataValue); const shouldThrowAnError = (i === (invocations - 1)); if (!shouldThrowAnError) { // Assert that the metadata object contains the response - expect(metadata[expectedServiceName]['index.handler response']) + expect(metadata[serviceNameWithAllFlagsEnabled]['index.handler response']) .toEqual(expectedCustomResponseValue); } } }, TEST_CASE_TIMEOUT); + + it('should not capture error nor response when the flags are false', async () => { + + const tracesWithNoCaptureErrorOrResponse = await getTraces(xray, startTime, await getFunctionArn(functionNameWithNoCaptureErrorOrResponse), invocations, 5); + + expect(tracesWithNoCaptureErrorOrResponse.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWithNoCaptureErrorOrResponse[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 3 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(3); + + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + // Assert that the subsegment has the expected fault + expect(invocationSubsegment.error).toBe(true); + expect(handlerSubsegment.error).toBe(true); + // Assert that no error was captured on the subsegment + expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); + } + } + + }, TEST_CASE_TIMEOUT); + + it('should not capture any custom traces when disabled', async () => { + const expectedNoOfTraces = 2; + const tracesWithTracerDisabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithTracerDisabled), invocations, expectedNoOfTraces); + + expect(tracesWithTracerDisabled.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWithTracerDisabled[i]; + expect(trace.Segments.length).toBe(2); + + /** + * Expect no subsegment in the invocation + */ + const invocationSubsegment = getInvocationSubsegment(trace); + expect(invocationSubsegment?.subsegments).toBeUndefined(); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + expect(invocationSubsegment.error).toBe(true); + } + } + + }, TEST_CASE_TIMEOUT); }); diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts index 5b4fbc5f0a..09be8872f5 100644 --- a/packages/tracing/tests/e2e/constants.ts +++ b/packages/tracing/tests/e2e/constants.ts @@ -1,6 +1,6 @@ export const RESOURCE_NAME_PREFIX = 'Tracer-E2E'; export const ONE_MINUTE = 60 * 1_000; -export const TEST_CASE_TIMEOUT = ONE_MINUTE * 2; +export const TEST_CASE_TIMEOUT = ONE_MINUTE * 3; export const SETUP_TIMEOUT = ONE_MINUTE * 5; export const TEARDOWN_TIMEOUT = ONE_MINUTE * 3; diff --git a/packages/tracing/tests/e2e/tracer.test.ts b/packages/tracing/tests/e2e/tracer.test.ts index 5c32204b67..4cef77d398 100644 --- a/packages/tracing/tests/e2e/tracer.test.ts +++ b/packages/tracing/tests/e2e/tracer.test.ts @@ -149,247 +149,6 @@ // }, TEARDOWN_TIMEOUT); -// it('Verifies that a when Tracer is used to manually instrument a function all custom traces are generated with correct annotations and metadata', async () => { - -// const resourceArn = invocationsMap['Manual'].resourceArn; -// const expectedServiceName = invocationsMap['Manual'].serviceName; - -// // Assess -// // Retrieve traces from X-Ray using Resource ARN as filter -// const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// for (let i = 0; i < invocations; i++) { -// expect(sortedTraces[i].Segments.length).toBe(5); - -// const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// if (invocationSubsegment?.subsegments !== undefined) { -// expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// expect(handlerSubsegment.name).toBe('## index.handler'); - -// if (handlerSubsegment?.subsegments !== undefined) { -// expect(handlerSubsegment?.subsegments?.length).toBe(3); - -// const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); -// // Assert that there are exactly two subsegment with the name 'DynamoDB' -// expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // Assert that there is exactly one subsegment with the name 'httpbin.org' -// expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // Assert that there are exactly zero other subsegments -// expect(subsegments.get('other')?.length).toBe(0); - -// const { annotations, metadata } = handlerSubsegment; - -// if (annotations !== undefined && metadata !== undefined) { -// // Assert that the annotations are as expected -// expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// expect(annotations['Service']).toEqual(expectedServiceName); -// expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // Assert that the metadata object is as expected -// expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// .toEqual(expectedCustomMetadataValue); - -// if (i === invocations - 1) { -// // Assert that the subsegment has the expected fault -// expect(invocationSubsegment.error).toBe(true); -// expect(handlerSubsegment.fault).toBe(true); -// expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); -// expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); -// } else { -// // Assert that the metadata object contains the response -// expect(metadata[expectedServiceName]['index.handler response']) -// .toEqual(expectedCustomResponseValue); -// } -// } else { -// // Make test fail if there are no annotations or metadata -// expect('annotations !== undefined && metadata !== undefined') -// .toBe('annotations === undefined && metadata === undefined'); -// } -// } else { -// // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// expect('handlerSubsegment?.subsegments !== undefined') -// .toBe('handlerSubsegment?.subsegments === undefined'); -// } -// } else { -// // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// expect('invocationSubsegment?.subsegments !== undefined') -// .toBe('invocationSubsegment?.subsegments === undefined'); -// } -// } - -// }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when Tracer is used as middleware all custom traces are generated with correct annotations and metadata', async () => { - -// // const resourceArn = invocationsMap['Middleware'].resourceArn; -// // const expectedServiceName = invocationsMap['Middleware'].serviceName; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(5); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // if (invocationSubsegment?.subsegments !== undefined) { -// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// // expect(handlerSubsegment.name).toBe('## index.handler'); - -// // if (handlerSubsegment?.subsegments !== undefined) { -// // expect(handlerSubsegment?.subsegments?.length).toBe(3); - -// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); -// // // Assert that there are exactly two subsegment with the name 'DynamoDB' -// // expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // // Assert that there is exactly one subsegment with the name 'httpbin.org' -// // expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // // Assert that there are exactly zero other subsegments -// // expect(subsegments.get('other')?.length).toBe(0); - -// // const { annotations, metadata } = handlerSubsegment; - -// // if (annotations !== undefined && metadata !== undefined) { -// // // Assert that the annotations are as expected -// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// // expect(annotations['Service']).toEqual(expectedServiceName); -// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// // .toEqual(expectedCustomMetadataValue); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // expect(handlerSubsegment.fault).toBe(true); -// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); -// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); -// // } else { -// // // Assert that the metadata object contains the response -// // expect(metadata[expectedServiceName]['index.handler response']) -// // .toEqual(expectedCustomResponseValue); -// // } -// // } else { -// // // Make test fail if there are no annotations or metadata -// // expect('annotations !== undefined && metadata !== undefined') -// // .toBe('annotations === undefined && metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// // expect('handlerSubsegment?.subsegments !== undefined') -// // .toBe('handlerSubsegment?.subsegments === undefined'); -// // } -// // } else { -// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// // expect('invocationSubsegment?.subsegments !== undefined') -// // .toBe('invocationSubsegment?.subsegments === undefined'); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when Tracer is used as middleware, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - -// // const resourceArn = invocationsMap['Middleware-NoCaptureErrorResponse'].resourceArn; -// // const expectedServiceName = invocationsMap['Middleware-NoCaptureErrorResponse'].serviceName; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(5); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // if (invocationSubsegment?.subsegments !== undefined) { -// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// // expect(handlerSubsegment.name).toBe('## index.handler'); - -// // if (handlerSubsegment?.subsegments !== undefined) { -// // expect(handlerSubsegment?.subsegments?.length).toBe(3); - -// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org' ]); -// // // Assert that there are exactly two subsegment with the name 'DynamoDB' -// // expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // // Assert that there is exactly one subsegment with the name 'httpbin.org' -// // expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // // Assert that there are exactly zero other subsegments -// // expect(subsegments.get('other')?.length).toBe(0); - -// // const { annotations, metadata } = handlerSubsegment; - -// // if (annotations !== undefined && metadata !== undefined) { -// // // Assert that the annotations are as expected -// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// // expect(annotations['Service']).toEqual(expectedServiceName); -// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// // .toEqual(expectedCustomMetadataValue); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // expect(handlerSubsegment.error).toBe(true); -// // // Assert that no error was captured on the subsegment -// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); -// // } else { -// // // Assert that the metadata object does not contain the response object -// // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); -// // } -// // } else { -// // // Make test fail if there are no annotations or metadata -// // expect('annotations !== undefined && metadata !== undefined') -// // .toBe('annotations === undefined && metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// // expect('handlerSubsegment?.subsegments !== undefined') -// // .toBe('handlerSubsegment?.subsegments === undefined'); -// // } -// // } else { -// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// // expect('invocationSubsegment?.subsegments !== undefined') -// // .toBe('invocationSubsegment?.subsegments === undefined'); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when tracing is disabled in middleware mode no custom traces are generated', async () => { - -// // const resourceArn = invocationsMap['Middleware-Disabled'].resourceArn; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(2); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // expect(invocationSubsegment?.subsegments).toBeUndefined(); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - // // it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { // // const resourceArn = invocationsMap['Decorator'].resourceArn; diff --git a/packages/tracing/tests/helpers/tracesUtils.ts b/packages/tracing/tests/helpers/tracesUtils.ts index 861cb25451..467b50b9f8 100644 --- a/packages/tracing/tests/helpers/tracesUtils.ts +++ b/packages/tracing/tests/helpers/tracesUtils.ts @@ -235,11 +235,14 @@ const createTracerTestFunction = (params: TracerTestFunctionParams): NodejsFunct return func; }; +let account: string | undefined; const getFunctionArn = async (functionName: string): Promise => { - const stsClient = new AWS.STS(); const region = process.env.AWS_REGION; - const identity = await stsClient.getCallerIdentity().promise(); - const account = identity.Account; + const stsClient = new AWS.STS(); + if (!account) { + const identity = await stsClient.getCallerIdentity().promise(); + account = identity.Account; + } return `arn:aws:lambda:${region}:${account}:function:${functionName}`; }; From 90c455a2cfd877e5d5a704420f54249d9ee38cc0 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 16:41:27 +0200 Subject: [PATCH 07/12] test(tracer): Add decorator cases --- ...llFeatures.decorator.test.functionCode.ts} | 0 .../tests/e2e/allFeatures.decorator.test.ts | 332 ++++++++++++++++++ .../tests/e2e/allFeatures.middy.test.ts | 2 +- 3 files changed, 333 insertions(+), 1 deletion(-) rename packages/tracing/tests/e2e/{tracer.test.Decorator.ts => allFeatures.decorator.test.functionCode.ts} (100%) create mode 100644 packages/tracing/tests/e2e/allFeatures.decorator.test.ts diff --git a/packages/tracing/tests/e2e/tracer.test.Decorator.ts b/packages/tracing/tests/e2e/allFeatures.decorator.test.functionCode.ts similarity index 100% rename from packages/tracing/tests/e2e/tracer.test.Decorator.ts rename to packages/tracing/tests/e2e/allFeatures.decorator.test.functionCode.ts diff --git a/packages/tracing/tests/e2e/allFeatures.decorator.test.ts b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts new file mode 100644 index 0000000000..a83a183940 --- /dev/null +++ b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts @@ -0,0 +1,332 @@ +/** + * Test tracer in decorator setup + * + * @group e2e/tracer/decorator + */ + +import { randomUUID } from 'crypto'; +import path from 'path'; +import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; +import * as AWS from 'aws-sdk'; +import { + getTraces, + getInvocationSubsegment, + splitSegmentsByName, + invokeAllTestCases, + createTracerTestFunction, + getFunctionArn, + getFirstSubsegment, +} from '../helpers/tracesUtils'; +import { + generateUniqueName, + isValidRuntimeKey, +} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, + expectedCustomErrorMessage, +} from './constants'; +import { + assertAnnotation, + assertErrorAndFault, +} from '../helpers/traceAssertions'; + +const runtime: string = process.env.RUNTIME || 'nodejs14x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +/** + * We will create a stack with 3 Lambda functions: + * 1. With all flags enabled (capture both response and error) + * 2. Do not capture error or response + * 3. Do not enable tracer + */ +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, randomUUID(), runtime, 'AllFeatures-Decorator'); +const lambdaFunctionCodeFile = 'allFeatures.decorator.test.functionCode.ts'; +let startTime: Date; + +/** + * Function #1 is with all flags enabled. + */ +const uuidFunction1 = randomUUID(); +const functionNameWithAllFlagsEnabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction1, runtime, 'AllFeatures-Decoratory-AllFlagsEnabled'); +const serviceNameWithAllFlagsEnabled = functionNameWithAllFlagsEnabled; + +/** + * Function #2 doesn't capture error or response + */ +const uuidFunction2 = randomUUID(); +const functionNameWithNoCaptureErrorOrResponse = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction2, runtime, 'AllFeatures-Decorator-NoCaptureErrorOrResponse'); +const serviceNameWithNoCaptureErrorOrResponse = functionNameWithNoCaptureErrorOrResponse; +/** + * Function #3 disables tracer + */ +const uuidFunction3 = randomUUID(); +const functionNameWithTracerDisabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction3, runtime, 'AllFeatures-Decorator-TracerDisabled'); +const serviceNameWithTracerDisabled = functionNameWithNoCaptureErrorOrResponse; + +const xray = new AWS.XRay(); +const invocations = 3; + +const integTestApp = new App(); +let stack: Stack; + +describe(`Tracer E2E tests, all features with decorator instantiation for runtime: ${runtime}`, () => { + + beforeAll(async () => { + + // Prepare + startTime = new Date(); + const ddbTableName = stackName + '-table'; + stack = new Stack(integTestApp, stackName); + + /** + * We need a DynamoDB table to connect via SDK so we can trace a PutItem call. + */ + const ddbTable = new Table(stack, 'Table', { + tableName: ddbTableName, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY + }); + + const entry = path.join(__dirname, lambdaFunctionCodeFile); + const functionWithAllFlagsEnabled = createTracerTestFunction({ + stack, + functionName: functionNameWithAllFlagsEnabled, + entry, + expectedServiceName: serviceNameWithAllFlagsEnabled, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + }, + runtime + }); + ddbTable.grantWriteData(functionWithAllFlagsEnabled); + + const functionThatDoesNotCapturesErrorAndResponse = createTracerTestFunction({ + stack, + functionName: functionNameWithNoCaptureErrorOrResponse, + entry, + expectedServiceName: serviceNameWithNoCaptureErrorOrResponse, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'false', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'false', + POWERTOOLS_TRACE_ENABLED: 'true', + }, + runtime + }); + ddbTable.grantWriteData(functionThatDoesNotCapturesErrorAndResponse); + + const functionWithTracerDisabled = createTracerTestFunction({ + stack, + functionName: functionNameWithTracerDisabled, + entry, + expectedServiceName: serviceNameWithTracerDisabled, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'false', + }, + runtime + }); + ddbTable.grantWriteData(functionWithTracerDisabled); + + await deployStack(integTestApp, stack); + + // Act + await Promise.all([ + invokeAllTestCases(functionNameWithAllFlagsEnabled), + invokeAllTestCases(functionNameWithNoCaptureErrorOrResponse), + invokeAllTestCases(functionNameWithTracerDisabled), + ]); + + }, SETUP_TIMEOUT); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + + it('should generate all custom traces', async () => { + + const tracesWhenAllFlagsEnabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithAllFlagsEnabled), invocations, 5); + + expect(tracesWhenAllFlagsEnabled.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWhenAllFlagsEnabled[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 4 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + * 4. '### myMethod' (method decorator) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(4); + + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('### myMethod')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + assertErrorAndFault(invocationSubsegment, expectedCustomErrorMessage); + } + } + + }, TEST_CASE_TIMEOUT); + + it('should have correct annotations and metadata', async () => { + const tracesWhenAllFlagsEnabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithAllFlagsEnabled), invocations, 5); + + for (let i = 0; i < invocations; i++) { + const trace = tracesWhenAllFlagsEnabled[i]; + const invocationSubsegment = getInvocationSubsegment(trace); + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + const { annotations, metadata } = handlerSubsegment; + + const isColdStart = (i === 0); + assertAnnotation({ + annotations, + isColdStart, + expectedServiceName: serviceNameWithAllFlagsEnabled, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + }); + + if (!metadata) { + fail('metadata is missing'); + } + expect(metadata[serviceNameWithAllFlagsEnabled][expectedCustomMetadataKey]) + .toEqual(expectedCustomMetadataValue); + + const shouldThrowAnError = (i === (invocations - 1)); + if (!shouldThrowAnError) { + // Assert that the metadata object contains the response + expect(metadata[serviceNameWithAllFlagsEnabled]['index.handler response']) + .toEqual(expectedCustomResponseValue); + } + } + }, TEST_CASE_TIMEOUT); + + it('should not capture error nor response when the flags are false', async () => { + + const tracesWithNoCaptureErrorOrResponse = await getTraces(xray, startTime, await getFunctionArn(functionNameWithNoCaptureErrorOrResponse), invocations, 5); + + expect(tracesWithNoCaptureErrorOrResponse.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWithNoCaptureErrorOrResponse[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 4 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + * 4. '### myMethod' (method decorator) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(4); + + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('### myMethod')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + // Assert that the subsegment has the expected fault + expect(invocationSubsegment.error).toBe(true); + expect(handlerSubsegment.error).toBe(true); + // Assert that no error was captured on the subsegment + expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); + } + } + + }, TEST_CASE_TIMEOUT); + + it('should not capture any custom traces when disabled', async () => { + const expectedNoOfTraces = 2; + const tracesWithTracerDisabled = await getTraces(xray, startTime, await getFunctionArn(functionNameWithTracerDisabled), invocations, expectedNoOfTraces); + + expect(tracesWithTracerDisabled.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWithTracerDisabled[i]; + expect(trace.Segments.length).toBe(2); + + /** + * Expect no subsegment in the invocation + */ + const invocationSubsegment = getInvocationSubsegment(trace); + expect(invocationSubsegment?.subsegments).toBeUndefined(); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + expect(invocationSubsegment.error).toBe(true); + } + } + + }, TEST_CASE_TIMEOUT); +}); + diff --git a/packages/tracing/tests/e2e/allFeatures.middy.test.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.ts index ab8735276e..da5f2b444a 100644 --- a/packages/tracing/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.middy.test.ts @@ -1,5 +1,5 @@ /** - * Test tracer manual mode + * Test tracer in middy setup * * @group e2e/tracer/middy */ From 8516b88dc6c6f2b1128697ae2bff2ad210192d83 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 16:59:43 +0200 Subject: [PATCH 08/12] test:(tracer): add async handler test cases --- ...yncHandler.decorator.test.functionCode.ts} | 0 .../tests/e2e/asyncHandler.decorator.test.ts | 194 ++++++++++++++++++ 2 files changed, 194 insertions(+) rename packages/tracing/tests/e2e/{tracer.test.DecoratorWithAsyncHandler.ts => asyncHandler.decorator.test.functionCode.ts} (100%) create mode 100644 packages/tracing/tests/e2e/asyncHandler.decorator.test.ts diff --git a/packages/tracing/tests/e2e/tracer.test.DecoratorWithAsyncHandler.ts b/packages/tracing/tests/e2e/asyncHandler.decorator.test.functionCode.ts similarity index 100% rename from packages/tracing/tests/e2e/tracer.test.DecoratorWithAsyncHandler.ts rename to packages/tracing/tests/e2e/asyncHandler.decorator.test.functionCode.ts diff --git a/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts new file mode 100644 index 0000000000..2349d4ea8c --- /dev/null +++ b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts @@ -0,0 +1,194 @@ +/** + * Test tracer in decorator setup + * + * @group e2e/tracer/decorator-async-handler + */ + +import { randomUUID } from 'crypto'; +import path from 'path'; +import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; +import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; +import * as AWS from 'aws-sdk'; +import { + getTraces, + getInvocationSubsegment, + splitSegmentsByName, + invokeAllTestCases, + createTracerTestFunction, + getFunctionArn, + getFirstSubsegment, +} from '../helpers/tracesUtils'; +import { + generateUniqueName, + isValidRuntimeKey, +} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +import { + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, + expectedCustomErrorMessage, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + expectedCustomMetadataKey, + expectedCustomMetadataValue, + expectedCustomResponseValue, +} from './constants'; +import { + assertAnnotation, + assertErrorAndFault, +} from '../helpers/traceAssertions'; + +const runtime: string = process.env.RUNTIME || 'nodejs14x'; + +if (!isValidRuntimeKey(runtime)) { + throw new Error(`Invalid runtime key value: ${runtime}`); +} + +const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, randomUUID(), runtime, 'AllFeatures-Decorator'); +const lambdaFunctionCodeFile = 'asyncHandler.decorator.test.functionCode.ts'; +let startTime: Date; + +const uuid = randomUUID(); +const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'AllFeatures-Decoratory-AllFlagsEnabled'); +const serviceName = functionName; + +const xray = new AWS.XRay(); +const invocations = 3; + +const integTestApp = new App(); +let stack: Stack; + +describe(`Tracer E2E tests, all features with decorator instantiation for runtime: ${runtime}`, () => { + + beforeAll(async () => { + + // Prepare + startTime = new Date(); + const ddbTableName = stackName + '-table'; + stack = new Stack(integTestApp, stackName); + + const ddbTable = new Table(stack, 'Table', { + tableName: ddbTableName, + partitionKey: { + name: 'id', + type: AttributeType.STRING + }, + billingMode: BillingMode.PAY_PER_REQUEST, + removalPolicy: RemovalPolicy.DESTROY + }); + + const entry = path.join(__dirname, lambdaFunctionCodeFile); + const functionWithAllFlagsEnabled = createTracerTestFunction({ + stack, + functionName: functionName, + entry, + expectedServiceName: serviceName, + environmentParams: { + TEST_TABLE_NAME: ddbTableName, + POWERTOOLS_TRACER_CAPTURE_RESPONSE: 'true', + POWERTOOLS_TRACER_CAPTURE_ERROR: 'true', + POWERTOOLS_TRACE_ENABLED: 'true', + }, + runtime + }); + ddbTable.grantWriteData(functionWithAllFlagsEnabled); + + await deployStack(integTestApp, stack); + + // Act + await invokeAllTestCases(functionName); + + }, SETUP_TIMEOUT); + + afterAll(async () => { + if (!process.env.DISABLE_TEARDOWN) { + await destroyStack(integTestApp, stack); + } + }, TEARDOWN_TIMEOUT); + + it('should generate all custom traces', async () => { + + const tracesWhenAllFlagsEnabled = await getTraces(xray, startTime, await getFunctionArn(functionName), invocations, 5); + + expect(tracesWhenAllFlagsEnabled.length).toBe(invocations); + + // Assess + for (let i = 0; i < invocations; i++) { + const trace = tracesWhenAllFlagsEnabled[i]; + + /** + * Expect the trace to have 5 segments: + * 1. Lambda Context (AWS::Lambda) + * 2. Lambda Function (AWS::Lambda::Function) + * 3. DynamoDB (AWS::DynamoDB) + * 4. DynamoDB Table (AWS::DynamoDB::Table) + * 5. Remote call (httpbin.org) + */ + expect(trace.Segments.length).toBe(5); + const invocationSubsegment = getInvocationSubsegment(trace); + + /** + * Invocation subsegment should have a subsegment '## index.handler' (default behavior for PowerTool tracer) + * '## index.handler' subsegment should have 4 subsegments + * 1. DynamoDB (PutItem on the table) + * 2. DynamoDB (PutItem overhead) + * 3. httpbin.org (Remote call) + * 4. '### myMethod' (method decorator) + */ + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + expect(handlerSubsegment.name).toBe('## index.handler'); + expect(handlerSubsegment?.subsegments).toHaveLength(4); + + if (!handlerSubsegment.subsegments) { + fail('"## index.handler" subsegment should have subsegments'); + } + const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); + expect(subsegments.get('DynamoDB')?.length).toBe(2); + expect(subsegments.get('httpbin.org')?.length).toBe(1); + expect(subsegments.get('### myMethod')?.length).toBe(1); + expect(subsegments.get('other')?.length).toBe(0); + + const shouldThrowAnError = (i === (invocations - 1)); + if (shouldThrowAnError) { + assertErrorAndFault(invocationSubsegment, expectedCustomErrorMessage); + } + } + + }, TEST_CASE_TIMEOUT); + + it('should have correct annotations and metadata', async () => { + const traces = await getTraces(xray, startTime, await getFunctionArn(functionName), invocations, 5); + + for (let i = 0; i < invocations; i++) { + const trace = traces[i]; + const invocationSubsegment = getInvocationSubsegment(trace); + const handlerSubsegment = getFirstSubsegment(invocationSubsegment); + const { annotations, metadata } = handlerSubsegment; + + const isColdStart = (i === 0); + assertAnnotation({ + annotations, + isColdStart, + expectedServiceName: serviceName, + expectedCustomAnnotationKey, + expectedCustomAnnotationValue, + }); + + if (!metadata) { + fail('metadata is missing'); + } + expect(metadata[serviceName][expectedCustomMetadataKey]) + .toEqual(expectedCustomMetadataValue); + + const shouldThrowAnError = (i === (invocations - 1)); + if (!shouldThrowAnError) { + // Assert that the metadata object contains the response + expect(metadata[serviceName]['index.handler response']) + .toEqual(expectedCustomResponseValue); + } + } + }, TEST_CASE_TIMEOUT); +}); + From 6f4e50e335253d25c68da4c46943aaebff1df099 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 17:00:27 +0200 Subject: [PATCH 09/12] test:(tracer) remove unused code --- packages/tracing/tests/e2e/constants.ts | 17 +- packages/tracing/tests/e2e/tracer.test.ts | 430 ---------------------- 2 files changed, 1 insertion(+), 446 deletions(-) delete mode 100644 packages/tracing/tests/e2e/tracer.test.ts diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts index 09be8872f5..22ebcc06d0 100644 --- a/packages/tracing/tests/e2e/constants.ts +++ b/packages/tracing/tests/e2e/constants.ts @@ -9,19 +9,4 @@ export const expectedCustomAnnotationValue = 'myValue'; export const expectedCustomMetadataKey = 'myMetadata'; export const expectedCustomMetadataValue = { bar: 'baz' }; export const expectedCustomResponseValue = { foo: 'bar' }; -export const expectedCustomErrorMessage = 'An error has occurred'; - -/** - * A type that contains information for invoking a Lambda function, - * and retrieving the traces. - * - * We fill the information while creting Lambda functions with CDK, - * and reuse it later in the test cases - */ -export type InvocationMap = { - [key: string]: { - functionName: string - serviceName: string - resourceArn: string - } -}; \ No newline at end of file +export const expectedCustomErrorMessage = 'An error has occurred'; \ No newline at end of file diff --git a/packages/tracing/tests/e2e/tracer.test.ts b/packages/tracing/tests/e2e/tracer.test.ts deleted file mode 100644 index 4cef77d398..0000000000 --- a/packages/tracing/tests/e2e/tracer.test.ts +++ /dev/null @@ -1,430 +0,0 @@ -// /** -// * Test tracer manual mode -// * -// * @group e2e/tracer/all -// */ - -// import { randomUUID, randomBytes } from 'crypto'; -// import { join } from 'path'; -// import { Tracing, Architecture } from 'aws-cdk-lib/aws-lambda'; -// import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; -// import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -// import { App, Duration, Stack, RemovalPolicy } from 'aws-cdk-lib'; -// import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; -// import * as AWS from 'aws-sdk'; -// import { getTraces, getInvocationSubsegment, splitSegmentsByName } from '../helpers/tracesUtils'; -// import { isValidRuntimeKey } from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; -// import { -// SETUP_TIMEOUT, -// TEARDOWN_TIMEOUT, -// TEST_CASE_TIMEOUT, -// expectedCustomAnnotationKey, -// expectedCustomAnnotationValue, -// expectedCustomMetadataKey, -// expectedCustomMetadataValue, -// expectedCustomResponseValue, -// expectedCustomErrorMessage, -// InvocationMap, -// } from './constants'; - -// const xray = new AWS.XRay(); -// const lambdaClient = new AWS.Lambda(); -// const stsClient = new AWS.STS(); - -// const runtime: string = process.env.RUNTIME || 'nodejs14x'; - -// if (!isValidRuntimeKey(runtime)) { -// throw new Error(`Invalid runtime key value: ${runtime}`); -// } - -// describe(`Tracer E2E tests for runtime: ${runtime}`, () => { - -// const startTime = new Date(); -// const invocations = 3; - -// let integTestApp: App; -// let stack: Stack; -// let invocationsMap: InvocationMap; - -// const addLambdaFunctions = async (stack: Stack, functionConfigs: string[], tableWithWriteAccess: Table): Promise => { -// const region = process.env.AWS_REGION; -// const identity = await stsClient.getCallerIdentity().promise(); -// const account = identity.Account; - -// const map: InvocationMap = {}; - -// for (const functionConfig of functionConfigs) { -// const expectedServiceName = randomUUID(); -// const fileName = functionConfig.split('-')[0]; -// const functionInstanceName = `${functionConfig}-${randomBytes(12).toString('hex')}`; -// const fn = new NodejsFunction(stack, functionConfig, { -// entry: join(__dirname, `tracer.test.${fileName}.ts`), -// handler: 'handler', -// functionName: functionInstanceName, -// tracing: Tracing.ACTIVE, -// architecture: Architecture.X86_64, -// memorySize: 256, -// environment: { -// EXPECTED_SERVICE_NAME: expectedServiceName, -// EXPECTED_CUSTOM_ANNOTATION_KEY: expectedCustomAnnotationKey, -// EXPECTED_CUSTOM_ANNOTATION_VALUE: expectedCustomAnnotationValue, -// EXPECTED_CUSTOM_METADATA_KEY: expectedCustomMetadataKey, -// EXPECTED_CUSTOM_METADATA_VALUE: JSON.stringify(expectedCustomMetadataValue), -// EXPECTED_CUSTOM_RESPONSE_VALUE: JSON.stringify(expectedCustomResponseValue), -// EXPECTED_CUSTOM_ERROR_MESSAGE: expectedCustomErrorMessage, -// POWERTOOLS_TRACER_CAPTURE_RESPONSE: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', -// POWERTOOLS_TRACER_CAPTURE_ERROR: functionConfig.indexOf('NoCaptureErrorResponse') !== -1 ? 'false' : 'true', -// POWERTOOLS_TRACE_ENABLED: functionConfig.indexOf('Disabled') !== -1 ? 'false' : 'true', -// TEST_TABLE_NAME: tableWithWriteAccess.tableName, -// }, -// timeout: Duration.seconds(30), -// bundling: { -// externalModules: ['aws-sdk'], -// } -// }); -// tableWithWriteAccess.grantWriteData(fn); - -// map[functionConfig] = { -// serviceName: expectedServiceName, -// functionName: functionInstanceName, -// resourceArn: `arn:aws:lambda:${region}:${account}:function:${functionInstanceName}`, // ARN is still a token at this point, so we construct the ARN manually -// }; -// } - -// return map; -// }; - -// beforeAll(async () => { - -// // Prepare -// integTestApp = new App(); -// stack = new Stack(integTestApp, 'TracerIntegTest'); // TODO: change stack name to be unique - -// const table = new Table(stack, 'Table', { -// tableName: randomUUID(), -// partitionKey: { -// name: 'id', -// type: AttributeType.STRING -// }, -// billingMode: BillingMode.PAY_PER_REQUEST, -// removalPolicy: RemovalPolicy.DESTROY -// }); - -// const functionConfigs = [ -// 'Manual', -// 'Middleware', -// 'Middleware-Disabled', -// 'Middleware-NoCaptureErrorResponse', -// 'Decorator', -// 'DecoratorWithAsyncHandler', -// 'Decorator-Disabled', -// 'Decorator-NoCaptureErrorResponse', -// ]; -// invocationsMap = await addLambdaFunctions(stack, functionConfigs, table); - -// await deployStack(integTestApp, stack); - -// // Act -// Object.values(invocationsMap).forEach(async ({ functionName }) => { -// for (let i = 0; i < invocations; i++) { -// await lambdaClient.invoke({ -// FunctionName: functionName, -// LogType: 'Tail', -// Payload: JSON.stringify({ -// throw: i === invocations - 1 ? true : false, // only last invocation should throw -// sdkV2: i === 1 ? 'all' : 'client', // only second invocation should use captureAll -// invocation: i + 1, // Pass invocation number for easier debugging -// }), -// }).promise(); -// } -// }); - -// }, SETUP_TIMEOUT); - -// afterAll(async () => { - -// if (!process.env.DISABLE_TEARDOWN) { -// await destroyStack(integTestApp, stack); -// } - -// }, TEARDOWN_TIMEOUT); - -// // it('Verifies that a when Tracer is used as decorator all custom traces are generated with correct annotations and metadata', async () => { - -// // const resourceArn = invocationsMap['Decorator'].resourceArn; -// // const expectedServiceName = invocationsMap['Decorator'].serviceName; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(5); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // if (invocationSubsegment?.subsegments !== undefined) { -// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// // expect(handlerSubsegment.name).toBe('## index.handler'); - -// // if (handlerSubsegment?.subsegments !== undefined) { -// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - -// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); -// // // Assert that there are exactly two subsegment with the name 'DynamoDB' -// // expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // // Assert that there is exactly one subsegment with the name 'httpbin.org' -// // expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // // Assert that there is exactly one subsegment with the name '### myMethod' -// // expect(subsegments.get('### myMethod')?.length).toBe(1); -// // // Assert that there are exactly zero other subsegments -// // expect(subsegments.get('other')?.length).toBe(0); - -// // const methodSubsegment = subsegments.get('### myMethod') || []; -// // const { metadata } = methodSubsegment[0]; - -// // if (metadata !== undefined) { -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName]['myMethod response']) -// // .toEqual(expectedCustomResponseValue); -// // } else { -// // // Make test fail if there is no metadata -// // expect('metadata !== undefined') -// // .toBe('metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// // expect('handlerSubsegment?.subsegments !== undefined') -// // .toBe('handlerSubsegment?.subsegments === undefined'); -// // } - -// // const { annotations, metadata } = handlerSubsegment; - -// // if (annotations !== undefined && metadata !== undefined) { -// // // Assert that the annotations are as expected -// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// // expect(annotations['Service']).toEqual(expectedServiceName); -// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// // .toEqual(expectedCustomMetadataValue); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // expect(handlerSubsegment.fault).toBe(true); -// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); -// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); -// // } else { -// // // Assert that the metadata object contains the response -// // expect(metadata[expectedServiceName]['index.handler response']) -// // .toEqual(expectedCustomResponseValue); -// // } -// // } else { -// // // Make test fail if there are no annotations or metadata -// // expect('annotations !== undefined && metadata !== undefined') -// // .toBe('annotations === undefined && metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// // expect('invocationSubsegment?.subsegments !== undefined') -// // .toBe('invocationSubsegment?.subsegments === undefined'); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when Tracer is used as decorator on an async handler all custom traces are generated with correct annotations and metadata', async () => { - -// // const resourceArn = invocationsMap['DecoratorWithAsyncHandler'].resourceArn; -// // const expectedServiceName = invocationsMap['DecoratorWithAsyncHandler'].serviceName; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(5); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // if (invocationSubsegment?.subsegments !== undefined) { -// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// // expect(handlerSubsegment.name).toBe('## index.handler'); - -// // if (handlerSubsegment?.subsegments !== undefined) { -// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - -// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); -// // // Assert that there are exactly two subsegment with the name 'DynamoDB' -// // expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // // Assert that there is exactly one subsegment with the name 'httpbin.org' -// // expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // // Assert that there is exactly one subsegment with the name '### myMethod' -// // expect(subsegments.get('### myMethod')?.length).toBe(1); -// // // Assert that there are exactly zero other subsegments -// // expect(subsegments.get('other')?.length).toBe(0); - -// // const methodSubsegment = subsegments.get('### myMethod') || []; -// // const { metadata } = methodSubsegment[0]; - -// // if (metadata !== undefined) { -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName]['myMethod response']) -// // .toEqual(expectedCustomResponseValue); -// // } else { -// // // Make test fail if there is no metadata -// // expect('metadata !== undefined') -// // .toBe('metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// // expect('handlerSubsegment?.subsegments !== undefined') -// // .toBe('handlerSubsegment?.subsegments === undefined'); -// // } - -// // const { annotations, metadata } = handlerSubsegment; - -// // if (annotations !== undefined && metadata !== undefined) { -// // // Assert that the annotations are as expected -// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// // expect(annotations['Service']).toEqual(expectedServiceName); -// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// // .toEqual(expectedCustomMetadataValue); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // expect(handlerSubsegment.fault).toBe(true); -// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true); -// // expect(handlerSubsegment.cause?.exceptions[0].message).toBe(expectedCustomErrorMessage); -// // } else { -// // // Assert that the metadata object contains the response -// // expect(metadata[expectedServiceName]['index.handler response']) -// // .toEqual(expectedCustomResponseValue); -// // } -// // } else { -// // // Make test fail if there are no annotations or metadata -// // expect('annotations !== undefined && metadata !== undefined') -// // .toBe('annotations === undefined && metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// // expect('invocationSubsegment?.subsegments !== undefined') -// // .toBe('invocationSubsegment?.subsegments === undefined'); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when Tracer is used as decorator, with errors & response capturing disabled, all custom traces are generated with correct annotations', async () => { - -// // const resourceArn = invocationsMap['Decorator-NoCaptureErrorResponse'].resourceArn; -// // const expectedServiceName = invocationsMap['Decorator-NoCaptureErrorResponse'].serviceName; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 5); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(5); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // if (invocationSubsegment?.subsegments !== undefined) { -// // expect(invocationSubsegment?.subsegments?.length).toBe(1); - -// // const handlerSubsegment = invocationSubsegment?.subsegments[0]; -// // expect(handlerSubsegment.name).toBe('## index.handler'); - -// // if (handlerSubsegment?.subsegments !== undefined) { -// // expect(handlerSubsegment?.subsegments?.length).toBe(4); - -// // const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', 'httpbin.org', '### myMethod' ]); -// // // Assert that there are exactly two subsegment with the name 'DynamoDB' -// // expect(subsegments.get('DynamoDB')?.length).toBe(2); -// // // Assert that there is exactly one subsegment with the name 'httpbin.org' -// // expect(subsegments.get('httpbin.org')?.length).toBe(1); -// // // Assert that there is exactly one subsegment with the name '### myMethod' -// // expect(subsegments.get('### myMethod')?.length).toBe(1); -// // // Assert that there are exactly zero other subsegments -// // expect(subsegments.get('other')?.length).toBe(0); - -// // // Assert that no response was captured on the subsegment -// // const methodSubsegment = subsegments.get('### myMethod') || []; -// // expect(methodSubsegment[0].hasOwnProperty('metadata')).toBe(false); -// // } else { -// // // Make test fail if the handlerSubsegment subsegment doesn't have any subsebment -// // expect('handlerSubsegment?.subsegments !== undefined') -// // .toBe('handlerSubsegment?.subsegments === undefined'); -// // } - -// // const { annotations, metadata } = handlerSubsegment; - -// // if (annotations !== undefined && metadata !== undefined) { -// // // Assert that the annotations are as expected -// // expect(annotations['ColdStart']).toEqual(true ? i === 0 : false); -// // expect(annotations['Service']).toEqual(expectedServiceName); -// // expect(annotations[expectedCustomAnnotationKey]).toEqual(expectedCustomAnnotationValue); -// // // Assert that the metadata object is as expected -// // expect(metadata[expectedServiceName][expectedCustomMetadataKey]) -// // .toEqual(expectedCustomMetadataValue); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // expect(handlerSubsegment.error).toBe(true); -// // // Assert that no error was captured on the subsegment -// // expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false); -// // } else { -// // // Assert that the metadata object does not contain the response object -// // expect(metadata[expectedServiceName].hasOwnProperty('index.handler response')).toBe(false); -// // } -// // } else { -// // // Make test fail if there are no annotations or metadata -// // expect('annotations !== undefined && metadata !== undefined') -// // .toBe('annotations === undefined && metadata === undefined'); -// // } -// // } else { -// // // Make test fail if the Invocation subsegment doesn't have an handler subsebment -// // expect('invocationSubsegment?.subsegments !== undefined') -// // .toBe('invocationSubsegment?.subsegments === undefined'); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// // it('Verifies that a when tracing is disabled in decorator mode no custom traces are generated', async () => { - -// // const resourceArn = invocationsMap['Decorator-Disabled'].resourceArn; - -// // // Assess -// // // Retrieve traces from X-Ray using Resource ARN as filter -// // const sortedTraces = await getTraces(xray, startTime, resourceArn, invocations, 2); - -// // for (let i = 0; i < invocations; i++) { -// // // Assert that the trace has the expected amount of segments -// // expect(sortedTraces[i].Segments.length).toBe(2); - -// // const invocationSubsegment = getInvocationSubsegment(sortedTraces[i]); - -// // expect(invocationSubsegment?.subsegments).toBeUndefined(); - -// // if (i === invocations - 1) { -// // // Assert that the subsegment has the expected fault -// // expect(invocationSubsegment.error).toBe(true); -// // } -// // } - -// // }, TEST_CASE_TIMEOUT); - -// }); \ No newline at end of file From 9f4391cb151a345d4d1c030511f7cf4106a679f1 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 17:17:11 +0200 Subject: [PATCH 10/12] test(tracer): update comment + fix incorrect test case name --- packages/tracing/tests/e2e/allFeatures.decorator.test.ts | 5 ++--- packages/tracing/tests/e2e/allFeatures.middy.test.ts | 5 ++--- packages/tracing/tests/e2e/asyncHandler.decorator.test.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/tracing/tests/e2e/allFeatures.decorator.test.ts b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts index a83a183940..014978d413 100644 --- a/packages/tracing/tests/e2e/allFeatures.decorator.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts @@ -51,6 +51,8 @@ if (!isValidRuntimeKey(runtime)) { * 1. With all flags enabled (capture both response and error) * 2. Do not capture error or response * 3. Do not enable tracer + * Each stack must use a unique `serviceName` as it's used to for retrieving the trace. + * Using the same one will result in traces from different test cases mixing up. */ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, randomUUID(), runtime, 'AllFeatures-Decorator'); const lambdaFunctionCodeFile = 'allFeatures.decorator.test.functionCode.ts'; @@ -91,9 +93,6 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim const ddbTableName = stackName + '-table'; stack = new Stack(integTestApp, stackName); - /** - * We need a DynamoDB table to connect via SDK so we can trace a PutItem call. - */ const ddbTable = new Table(stack, 'Table', { tableName: ddbTableName, partitionKey: { diff --git a/packages/tracing/tests/e2e/allFeatures.middy.test.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.ts index da5f2b444a..13f8b72880 100644 --- a/packages/tracing/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.middy.test.ts @@ -51,6 +51,8 @@ if (!isValidRuntimeKey(runtime)) { * 1. With all flags enabled (capture both response and error) * 2. Do not capture error or response * 3. Do not enable tracer + * Each stack must use a unique `serviceName` as it's used to for retrieving the trace. + * Using the same one will result in traces from different test cases mixing up. */ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, randomUUID(), runtime, 'AllFeatures-Middy'); const lambdaFunctionCodeFile = 'allFeatures.middy.test.functionCode.ts'; @@ -91,9 +93,6 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ const ddbTableName = stackName + '-table'; stack = new Stack(integTestApp, stackName); - /** - * We need a DynamoDB table to connect via SDK so we can trace a PutItem call. - */ const ddbTable = new Table(stack, 'Table', { tableName: ddbTableName, partitionKey: { diff --git a/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts index 2349d4ea8c..f28c3abd03 100644 --- a/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts +++ b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts @@ -60,7 +60,7 @@ const invocations = 3; const integTestApp = new App(); let stack: Stack; -describe(`Tracer E2E tests, all features with decorator instantiation for runtime: ${runtime}`, () => { +describe(`Tracer E2E tests, asynchronous handler with decorator instantiation for runtime: ${runtime}`, () => { beforeAll(async () => { From 6e571a35aef76461924f7ea08e500d5f4a96dc89 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Wed, 20 Apr 2022 17:25:07 +0200 Subject: [PATCH 11/12] test(tracer): add targets to run test in both runtimes --- packages/tracing/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 408167e49d..266ca32a8a 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -13,7 +13,9 @@ "commit": "commit", "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", - "test:e2e": "jest --group=e2e", + "test:e2e:nodejs12x": "RUNTIME=nodejs12x jest --group=e2e", + "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", + "test:e2e": "concurrently \"npm:test:e2e:nodejs12x\" \"npm:test:e2e:nodejs14x\"", "watch": "jest --watch", "build": "tsc", "lint": "eslint --ext .ts --fix --no-error-on-unmatched-pattern src tests", From 73e8e87d0dc6ea73718c272802991e12dab8a562 Mon Sep 17 00:00:00 2001 From: ijemmy Date: Fri, 22 Apr 2022 14:24:47 +0200 Subject: [PATCH 12/12] test(tracer): make import relative and put relative imports after --- .../tracing/tests/e2e/allFeatures.decorator.test.ts | 4 ++-- .../tracing/tests/e2e/allFeatures.manual.test.ts | 13 +++++++++++-- .../tracing/tests/e2e/allFeatures.middy.test.ts | 4 ++-- .../tests/e2e/asyncHandler.decorator.test.ts | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/tracing/tests/e2e/allFeatures.decorator.test.ts b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts index 014978d413..5869c46e78 100644 --- a/packages/tracing/tests/e2e/allFeatures.decorator.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.decorator.test.ts @@ -8,8 +8,8 @@ import { randomUUID } from 'crypto'; import path from 'path'; import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; -import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; import { getTraces, getInvocationSubsegment, @@ -22,7 +22,7 @@ import { import { generateUniqueName, isValidRuntimeKey, -} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +} from '../../../commons/tests/utils/e2eUtils'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, diff --git a/packages/tracing/tests/e2e/allFeatures.manual.test.ts b/packages/tracing/tests/e2e/allFeatures.manual.test.ts index 97d014a736..ba56577945 100644 --- a/packages/tracing/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.manual.test.ts @@ -8,9 +8,18 @@ import { randomUUID } from 'crypto'; import path from 'path'; import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; -import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; -import { getTraces, getInvocationSubsegment, splitSegmentsByName, ParsedTrace, invokeAllTestCases, createTracerTestFunction, getFunctionArn, getFirstSubsegment } from '../helpers/tracesUtils'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; +import { + getTraces, + getInvocationSubsegment, + splitSegmentsByName, + ParsedTrace, + invokeAllTestCases, + createTracerTestFunction, + getFunctionArn, + getFirstSubsegment, +} from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, diff --git a/packages/tracing/tests/e2e/allFeatures.middy.test.ts b/packages/tracing/tests/e2e/allFeatures.middy.test.ts index 13f8b72880..c4be1934f2 100644 --- a/packages/tracing/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracing/tests/e2e/allFeatures.middy.test.ts @@ -8,8 +8,8 @@ import { randomUUID } from 'crypto'; import path from 'path'; import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; -import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; import { getTraces, getInvocationSubsegment, @@ -22,7 +22,7 @@ import { import { generateUniqueName, isValidRuntimeKey, -} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +} from '../../../commons/tests/utils/e2eUtils'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, diff --git a/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts index f28c3abd03..46f8dedd7b 100644 --- a/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts +++ b/packages/tracing/tests/e2e/asyncHandler.decorator.test.ts @@ -8,8 +8,8 @@ import { randomUUID } from 'crypto'; import path from 'path'; import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; -import { deployStack, destroyStack } from '@aws-lambda-powertools/commons/tests/utils/cdk-cli'; import * as AWS from 'aws-sdk'; +import { deployStack, destroyStack } from '../../../commons/tests/utils/cdk-cli'; import { getTraces, getInvocationSubsegment, @@ -22,7 +22,7 @@ import { import { generateUniqueName, isValidRuntimeKey, -} from '@aws-lambda-powertools/commons/tests/utils/e2eUtils'; +} from '../../../commons/tests/utils/e2eUtils'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT,