From 0ba340c0f2b7166e37c866d18fc820ab62610b30 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 22 Jul 2024 16:41:18 +0200 Subject: [PATCH 1/3] chore(maintenance): migrate tracer utility to biome --- packages/tracer/package.json | 13 +-- packages/tracer/src/Tracer.ts | 42 ++++----- .../src/config/EnvironmentVariablesService.ts | 2 +- packages/tracer/src/middleware/middy.ts | 8 +- .../tracer/src/provider/ProviderService.ts | 19 ++-- packages/tracer/src/provider/utilities.ts | 4 +- packages/tracer/src/types/ProviderService.ts | 2 +- packages/tracer/src/types/Tracer.ts | 12 +-- ...allFeatures.decorator.test.functionCode.ts | 8 +- .../tests/e2e/allFeatures.decorator.test.ts | 10 +- .../allFeatures.manual.test.functionCode.ts | 2 +- .../tests/e2e/allFeatures.manual.test.ts | 12 +-- .../allFeatures.middy.test.functionCode.ts | 38 ++++---- .../tests/e2e/allFeatures.middy.test.ts | 10 +- ...syncHandler.decorator.test.functionCode.ts | 40 ++++---- .../tests/e2e/asyncHandler.decorator.test.ts | 10 +- packages/tracer/tests/helpers/resources.ts | 4 +- .../tracer/tests/helpers/traceAssertions.ts | 6 +- packages/tracer/tests/helpers/tracesUtils.ts | 22 ++--- packages/tracer/tests/tsconfig.json | 17 ++-- .../unit/EnvironmentVariablesService.test.ts | 1 - .../tracer/tests/unit/ProviderService.test.ts | 14 +-- packages/tracer/tests/unit/Tracer.test.ts | 91 +++++++------------ packages/tracer/tests/unit/middy.test.ts | 16 ++-- packages/tracer/tsconfig.esm.json | 6 +- packages/tracer/tsconfig.json | 18 ++-- packages/tracer/typedoc.json | 2 +- 27 files changed, 187 insertions(+), 242 deletions(-) diff --git a/packages/tracer/package.json b/packages/tracer/package.json index 64f68c16e9..78d0a37e6b 100644 --- a/packages/tracer/package.json +++ b/packages/tracer/package.json @@ -20,8 +20,8 @@ "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", "build": "npm run build:esm & npm run build:cjs", - "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", - "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", + "lint": "biome lint .", + "lint:fix": "biome check --write .", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/tracer#readme", @@ -69,17 +69,12 @@ "lib/cjs/middleware/middy.d.ts", "lib/esm/middleware/middy.d.ts" ], - "types": [ - "lib/cjs/types/index.d.ts", - "lib/esm/types/index.d.ts" - ] + "types": ["lib/cjs/types/index.d.ts", "lib/esm/types/index.d.ts"] } }, "types": "./lib/cjs/index.d.ts", "main": "./lib/cjs/index.js", - "files": [ - "lib" - ], + "files": ["lib"], "repository": { "type": "git", "url": "git+https://github.com/aws-powertools/powertools-lambda-typescript.git" diff --git a/packages/tracer/src/Tracer.ts b/packages/tracer/src/Tracer.ts index e16840af40..004018344a 100644 --- a/packages/tracer/src/Tracer.ts +++ b/packages/tracer/src/Tracer.ts @@ -1,24 +1,24 @@ -import type { Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import type { AsyncHandler, - SyncHandler, HandlerMethodDecorator, + SyncHandler, } from '@aws-lambda-powertools/commons/types'; +import type { Handler } from 'aws-lambda'; +import type { Segment, Subsegment } from 'aws-xray-sdk-core'; +import xraySdk from 'aws-xray-sdk-core'; import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; +import { ProviderService } from './provider/ProviderService.js'; import type { ConfigServiceInterface } from './types/ConfigServiceInterface.js'; +import type { ProviderServiceInterface } from './types/ProviderService.js'; import type { - TracerInterface, - TracerOptions, AnyClass, - MethodDecorator, CaptureLambdaHandlerOptions, CaptureMethodOptions, + MethodDecorator, + TracerInterface, + TracerOptions, } from './types/Tracer.js'; -import { ProviderService } from './provider/ProviderService.js'; -import type { ProviderServiceInterface } from './types/ProviderService.js'; -import type { Segment, Subsegment } from 'aws-xray-sdk-core'; -import xraySdk from 'aws-xray-sdk-core'; const { Subsegment: XraySubsegment } = xraySdk; /** @@ -372,10 +372,7 @@ class Tracer extends Utility implements TracerInterface { options?: CaptureLambdaHandlerOptions ): HandlerMethodDecorator { return (_target, _propertyKey, descriptor) => { - /** - * The descriptor.value is the method this decorator decorates, it cannot be undefined. - */ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: The descriptor.value is the method this decorator decorates, it cannot be undefined. const originalMethod = descriptor.value!; // eslint-disable-next-line @typescript-eslint/no-this-alias @@ -383,11 +380,8 @@ class Tracer extends Utility implements TracerInterface { // Use a function() {} instead of an () => {} arrow function so that we can // access `myClass` as `this` in a decorated `myClass.myMethod()`. descriptor.value = function (this: Handler, event, context, callback) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const handlerRef: Handler = this; - if (!tracerRef.isTracingEnabled()) { - return originalMethod.apply(handlerRef, [event, context, callback]); + return originalMethod.apply(this, [event, context, callback]); } return tracerRef.provider.captureAsyncFunc( @@ -397,7 +391,7 @@ class Tracer extends Utility implements TracerInterface { tracerRef.addServiceNameAnnotation(); let result: unknown; try { - result = await originalMethod.apply(handlerRef, [ + result = await originalMethod.apply(this, [ event, context, callback, @@ -413,7 +407,7 @@ class Tracer extends Utility implements TracerInterface { subsegment?.close(); } catch (error) { console.warn( - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', subsegment?.name, error ); @@ -469,8 +463,7 @@ class Tracer extends Utility implements TracerInterface { options?: CaptureMethodOptions ): MethodDecorator { return (_target, propertyKey, descriptor) => { - // The descriptor.value is the method this decorator decorates, it cannot be undefined. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: The descriptor.value is the method this decorator decorates, it cannot be undefined. const originalMethod = descriptor.value!; // eslint-disable-next-line @typescript-eslint/no-this-alias @@ -490,7 +483,8 @@ class Tracer extends Utility implements TracerInterface { return tracerRef.provider.captureAsyncFunc( subsegmentName, async (subsegment) => { - let result; + // biome-ignore lint/suspicious/noExplicitAny: we don't know the type of the result because we're decorating arbitrary functions + let result: any; try { result = await originalMethod.apply(this, [...args]); if (options?.captureResponse ?? true) { @@ -505,7 +499,7 @@ class Tracer extends Utility implements TracerInterface { subsegment?.close(); } catch (error) { console.warn( - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', subsegment?.name, error ); @@ -702,7 +696,7 @@ class Tracer extends Utility implements TracerInterface { public setSegment(segment: Segment | Subsegment): void { if (!this.isTracingEnabled()) return; - return this.provider.setSegment(segment); + this.provider.setSegment(segment); } /** diff --git a/packages/tracer/src/config/EnvironmentVariablesService.ts b/packages/tracer/src/config/EnvironmentVariablesService.ts index d6d9bb7343..b1b1fcb65b 100644 --- a/packages/tracer/src/config/EnvironmentVariablesService.ts +++ b/packages/tracer/src/config/EnvironmentVariablesService.ts @@ -1,5 +1,5 @@ -import { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; +import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; class EnvironmentVariablesService extends CommonEnvironmentVariablesService diff --git a/packages/tracer/src/middleware/middy.ts b/packages/tracer/src/middleware/middy.ts index 16fd45676c..90207d7664 100644 --- a/packages/tracer/src/middleware/middy.ts +++ b/packages/tracer/src/middleware/middy.ts @@ -1,11 +1,11 @@ import { TRACER_KEY } from '@aws-lambda-powertools/commons'; -import type { Tracer } from '../Tracer.js'; -import type { Segment, Subsegment } from 'aws-xray-sdk-core'; -import type { CaptureLambdaHandlerOptions } from '../types/Tracer.js'; import type { MiddlewareLikeObj, MiddyLikeRequest, } from '@aws-lambda-powertools/commons/types'; +import type { Segment, Subsegment } from 'aws-xray-sdk-core'; +import type { Tracer } from '../Tracer.js'; +import type { CaptureLambdaHandlerOptions } from '../types/Tracer.js'; /** * A middy middleware automating capture of metadata and annotations on segments or subsegments for a Lambda Handler. @@ -75,7 +75,7 @@ const captureLambdaHandler = ( handlerSegment.close(); } catch (error) { console.warn( - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', handlerSegment.name, error ); diff --git a/packages/tracer/src/provider/ProviderService.ts b/packages/tracer/src/provider/ProviderService.ts index 6be83e1d62..7d5afffa96 100644 --- a/packages/tracer/src/provider/ProviderService.ts +++ b/packages/tracer/src/provider/ProviderService.ts @@ -1,11 +1,11 @@ +import type { Logger, Segment, Subsegment } from 'aws-xray-sdk-core'; +import xraySdk from 'aws-xray-sdk-core'; import type { Namespace } from 'cls-hooked'; import type { - ProviderServiceInterface, ContextMissingStrategy, HttpSubsegment, + ProviderServiceInterface, } from '../types/ProviderService.js'; -import type { Segment, Subsegment, Logger } from 'aws-xray-sdk-core'; -import xraySdk from 'aws-xray-sdk-core'; const { captureAWS, captureAWSClient, @@ -21,16 +21,16 @@ const { setDaemonAddress, setLogger, } = xraySdk; -import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; import { subscribe } from 'node:diagnostics_channel'; +import http from 'node:http'; +import https from 'node:https'; +import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; +import type { DiagnosticsChannel } from 'undici-types'; import { findHeaderAndDecode, getOriginURL, isHttpSubsegment, } from './utilities.js'; -import type { DiagnosticsChannel } from 'undici-types'; -import http from 'node:http'; -import https from 'node:https'; class ProviderService implements ProviderServiceInterface { /** @@ -50,8 +50,7 @@ class ProviderService implements ProviderServiceInterface { public captureAWSv3Client(service: T): T { addUserAgentMiddleware(service, 'tracer'); - // Type must be aliased as any because of this https://github.com/aws/aws-xray-sdk-node/issues/439#issuecomment-859715660 - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // biome-ignore lint/suspicious/noExplicitAny: Type must be aliased as any because of this https://github.com/aws/aws-xray-sdk-node/issues/439#issuecomment-859715660 return captureAWSv3Client(service as any); } @@ -150,7 +149,7 @@ class ProviderService implements ProviderServiceInterface { response: { status, ...(contentLenght && { - content_length: parseInt(contentLenght), + content_length: Number.parseInt(contentLenght), }), }, }; diff --git a/packages/tracer/src/provider/utilities.ts b/packages/tracer/src/provider/utilities.ts index 9e1780eb48..a4b9e8e587 100644 --- a/packages/tracer/src/provider/utilities.ts +++ b/packages/tracer/src/provider/utilities.ts @@ -1,6 +1,6 @@ -import type { HttpSubsegment } from '../types/ProviderService.js'; -import type { Segment, Subsegment } from 'aws-xray-sdk-core'; import { URL } from 'node:url'; +import type { Segment, Subsegment } from 'aws-xray-sdk-core'; +import type { HttpSubsegment } from '../types/ProviderService.js'; const decoder = new TextDecoder(); diff --git a/packages/tracer/src/types/ProviderService.ts b/packages/tracer/src/types/ProviderService.ts index edd6fa909a..f142bbe6e6 100644 --- a/packages/tracer/src/types/ProviderService.ts +++ b/packages/tracer/src/types/ProviderService.ts @@ -1,5 +1,5 @@ -import type { Namespace } from 'cls-hooked'; import type { Segment, Subsegment } from 'aws-xray-sdk-core'; +import type { Namespace } from 'cls-hooked'; type ContextMissingStrategy = | 'LOG_ERROR' diff --git a/packages/tracer/src/types/Tracer.ts b/packages/tracer/src/types/Tracer.ts index c7b4100431..8978e0bd6a 100644 --- a/packages/tracer/src/types/Tracer.ts +++ b/packages/tracer/src/types/Tracer.ts @@ -1,6 +1,6 @@ -import type { ConfigServiceInterface } from './ConfigServiceInterface.js'; import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; import type { Segment, Subsegment } from 'aws-xray-sdk-core'; +import type { ConfigServiceInterface } from './ConfigServiceInterface.js'; /** * Options for the tracer class to be used during initialization. @@ -99,9 +99,9 @@ type CaptureMethodOptions = { captureResponse?: boolean; }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: this is a generic type that is intentionally open type AnyClassMethod = (...args: any[]) => any; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: this is a generic type that is intentionally open type AnyClass = new (...args: any[]) => any; type MethodDecorator = ( @@ -115,9 +115,9 @@ interface TracerInterface { addResponseAsMetadata(data?: unknown, methodName?: string): void; addServiceNameAnnotation(): void; annotateColdStart(): void; - captureAWS(aws: T): void | T; - captureAWSv3Client(service: T): void | T; - captureAWSClient(service: T): void | T; + captureAWS(aws: T): undefined | T; + captureAWSv3Client(service: T): undefined | T; + captureAWSClient(service: T): undefined | T; captureLambdaHandler( options?: CaptureLambdaHandlerOptions ): HandlerMethodDecorator; diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts index c6f1734ff7..5719b37fc1 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts @@ -1,6 +1,6 @@ -import { Tracer } from '../../src/Tracer.js'; import type { Callback, Context } from 'aws-lambda'; import AWS from 'aws-sdk'; +import { Tracer } from '../../src/Tracer.js'; import { httpRequest } from '../helpers/httpRequest.js'; const serviceName = @@ -41,7 +41,7 @@ export class MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): void | Promise { + ): unknown | Promise { tracer.putAnnotation(customAnnotationKey, customAnnotationValue); tracer.putMetadata(customMetadataKey, customMetadataValue); @@ -84,7 +84,7 @@ class MyFunctionWithDecorator extends MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): void | Promise { + ): unknown | Promise { return super.handler(event, _context, _callback); } @@ -103,7 +103,7 @@ class MyFunctionWithDecoratorCaptureResponseFalse extends MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): void | Promise { + ): unknown | Promise { return super.handler(event, _context, _callback); } diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts index 9277dc64b4..d8160d8309 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts @@ -3,9 +3,9 @@ * * @group e2e/tracer/decorator */ +import { join } from 'node:path'; import { TestStack } from '@aws-lambda-powertools/testing-utils'; import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; -import { join } from 'node:path'; import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, @@ -19,11 +19,11 @@ import { splitSegmentsByName, } from '../helpers/tracesUtils.js'; import { - commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, + commonEnvironmentVars, } from './constants.js'; /** @@ -35,7 +35,7 @@ import { * 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. */ -describe(`Tracer E2E tests, all features with decorator instantiation`, () => { +describe('Tracer E2E tests, all features with decorator instantiation', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, @@ -265,14 +265,14 @@ describe(`Tracer E2E tests, all features with decorator instantiation`, () => { if (!metadata) { fail('metadata is missing'); } - expect(metadata['AllFlagsOn'][expectedCustomMetadataKey]).toEqual( + expect(metadata.AllFlagsOn[expectedCustomMetadataKey]).toEqual( expectedCustomMetadataValue ); const shouldThrowAnError = i === invocationCount - 1; if (!shouldThrowAnError) { // Assert that the metadata object contains the response - expect(metadata['AllFlagsOn']['index.handler response']).toEqual( + expect(metadata.AllFlagsOn['index.handler response']).toEqual( expectedCustomResponseValue ); } diff --git a/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts index 74983fe5f6..6cde17b438 100644 --- a/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts @@ -1,7 +1,7 @@ -import { Tracer } from '../../src/index.js'; import type { Context } from 'aws-lambda'; import AWS from 'aws-sdk'; import type { Subsegment } from 'aws-xray-sdk-core'; +import { Tracer } from '../../src/index.js'; import { httpRequest } from '../helpers/httpRequest.js'; const serviceName = diff --git a/packages/tracer/tests/e2e/allFeatures.manual.test.ts b/packages/tracer/tests/e2e/allFeatures.manual.test.ts index 280a1c3efd..5b5823f475 100644 --- a/packages/tracer/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.manual.test.ts @@ -3,31 +3,31 @@ * * @group e2e/tracer/manual */ +import { join } from 'node:path'; import { TestStack } from '@aws-lambda-powertools/testing-utils'; import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; -import { join } from 'node:path'; import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, assertErrorAndFault, } from '../helpers/traceAssertions.js'; import { + type ParsedTrace, getFirstSubsegment, getInvocationSubsegment, getTraces, invokeAllTestCases, splitSegmentsByName, - type ParsedTrace, } from '../helpers/tracesUtils.js'; import { - commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, + commonEnvironmentVars, } from './constants.js'; -describe(`Tracer E2E tests, all features with manual instantiation`, () => { +describe('Tracer E2E tests, all features with manual instantiation', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, @@ -171,14 +171,14 @@ describe(`Tracer E2E tests, all features with manual instantiation`, () => { if (!metadata) { fail('metadata is missing'); } - expect(metadata['AllFlagsOn'][expectedCustomMetadataKey]).toEqual( + expect(metadata.AllFlagsOn[expectedCustomMetadataKey]).toEqual( expectedCustomMetadataValue ); const shouldThrowAnError = i === invocationCount - 1; if (!shouldThrowAnError) { // Assert that the metadata object contains the response - expect(metadata['AllFlagsOn']['index.handler response']).toEqual( + expect(metadata.AllFlagsOn['index.handler response']).toEqual( expectedCustomResponseValue ); } diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts index 069831e8c8..07cc915c12 100644 --- a/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts @@ -1,8 +1,8 @@ +import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; +import type { Context } from 'aws-lambda'; import middy from 'middy5'; import { Tracer } from '../../src/index.js'; import { captureLambdaHandler } from '../../src/middleware/middy.js'; -import type { Context } from 'aws-lambda'; -import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; import { httpRequest } from '../helpers/httpRequest.js'; const serviceName = @@ -39,27 +39,23 @@ const testHandler = async ( tracer.putAnnotation(customAnnotationKey, customAnnotationValue); tracer.putMetadata(customMetadataKey, customMetadataValue); - try { - await dynamoDB.send( - new PutItemCommand({ - TableName: testTableName, - Item: { id: { S: `${serviceName}-${event.invocation}-sdkv3` } }, - }) - ); - await httpRequest({ - hostname: 'docs.powertools.aws.dev', - path: '/lambda/typescript/latest/', - }); - - const res = customResponseValue; - if (event.throw) { - throw new Error(customErrorMessage); - } + await dynamoDB.send( + new PutItemCommand({ + TableName: testTableName, + Item: { id: { S: `${serviceName}-${event.invocation}-sdkv3` } }, + }) + ); + await httpRequest({ + hostname: 'docs.powertools.aws.dev', + path: '/lambda/typescript/latest/', + }); - return res; - } catch (err) { - throw err; + const res = customResponseValue; + if (event.throw) { + throw new Error(customErrorMessage); } + + return res; }; export const handler = middy(testHandler).use(captureLambdaHandler(tracer)); diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.ts index 492a5c60aa..b22204e4aa 100644 --- a/packages/tracer/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.middy.test.ts @@ -3,9 +3,9 @@ * * @group e2e/tracer/middy */ +import { join } from 'node:path'; import { TestStack } from '@aws-lambda-powertools/testing-utils'; import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; -import { join } from 'node:path'; import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, @@ -19,11 +19,11 @@ import { splitSegmentsByName, } from '../helpers/tracesUtils.js'; import { - commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, + commonEnvironmentVars, } from './constants.js'; /** @@ -35,7 +35,7 @@ import { * 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. */ -describe(`Tracer E2E tests, all features with middy instantiation`, () => { +describe('Tracer E2E tests, all features with middy instantiation', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, @@ -262,14 +262,14 @@ describe(`Tracer E2E tests, all features with middy instantiation`, () => { if (!metadata) { fail('metadata is missing'); } - expect(metadata['AllFlagsOn'][expectedCustomMetadataKey]).toEqual( + expect(metadata.AllFlagsOn[expectedCustomMetadataKey]).toEqual( expectedCustomMetadataValue ); const shouldThrowAnError = i === invocationCount - 1; if (!shouldThrowAnError) { // Assert that the metadata object contains the response - expect(metadata['AllFlagsOn']['index.handler response']).toEqual( + expect(metadata.AllFlagsOn['index.handler response']).toEqual( expectedCustomResponseValue ); } diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts index 25d1b4b180..24319b914c 100644 --- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts +++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts @@ -1,7 +1,7 @@ -import { Tracer } from '../../src/index.js'; -import type { Context } from 'aws-lambda'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; +import type { Context } from 'aws-lambda'; +import { Tracer } from '../../src/index.js'; import { httpRequest } from '../helpers/httpRequest.js'; const serviceName = @@ -48,27 +48,23 @@ export class MyFunctionBase { tracer.putAnnotation(customAnnotationKey, customAnnotationValue); tracer.putMetadata(customMetadataKey, customMetadataValue); - try { - await dynamoDB.send( - new PutCommand({ - TableName: testTableName, - Item: { id: `${serviceName}-${event.invocation}-sdkv3` }, - }) - ); - await httpRequest({ - hostname: 'docs.powertools.aws.dev', - path: '/lambda/typescript/latest/', - }); - - const res = this.myMethod(); - if (event.throw) { - throw new Error(customErrorMessage); - } - - return res; - } catch (err) { - throw err; + await dynamoDB.send( + new PutCommand({ + TableName: testTableName, + Item: { id: `${serviceName}-${event.invocation}-sdkv3` }, + }) + ); + await httpRequest({ + hostname: 'docs.powertools.aws.dev', + path: '/lambda/typescript/latest/', + }); + + const res = this.myMethod(); + if (event.throw) { + throw new Error(customErrorMessage); } + + return res; } public myMethod(): string { diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts index 557031143a..e5ef278f8d 100644 --- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts +++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts @@ -3,9 +3,9 @@ * * @group e2e/tracer/decorator-async-handler */ +import { join } from 'node:path'; import { TestStack } from '@aws-lambda-powertools/testing-utils'; import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; -import { join } from 'node:path'; import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, @@ -19,14 +19,14 @@ import { splitSegmentsByName, } from '../helpers/tracesUtils.js'; import { - commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, + commonEnvironmentVars, } from './constants.js'; -describe(`Tracer E2E tests, async handler with decorator instantiation`, () => { +describe('Tracer E2E tests, async handler with decorator instantiation', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, @@ -208,14 +208,14 @@ describe(`Tracer E2E tests, async handler with decorator instantiation`, () => { if (!metadata) { fail('metadata is missing'); } - expect(metadata['AllFlagsOn'][expectedCustomMetadataKey]).toEqual( + expect(metadata.AllFlagsOn[expectedCustomMetadataKey]).toEqual( expectedCustomMetadataValue ); const shouldThrowAnError = i === invocationsCount - 1; if (!shouldThrowAnError) { // Assert that the metadata object contains the response - expect(metadata['AllFlagsOn']['index.handler response']).toEqual( + expect(metadata.AllFlagsOn['index.handler response']).toEqual( expectedCustomResponseValue ); } diff --git a/packages/tracer/tests/helpers/resources.ts b/packages/tracer/tests/helpers/resources.ts index a86e5211db..447ac238ee 100644 --- a/packages/tracer/tests/helpers/resources.ts +++ b/packages/tracer/tests/helpers/resources.ts @@ -1,9 +1,9 @@ -import { type TestStack } from '@aws-lambda-powertools/testing-utils'; +import type { TestStack } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import type { ExtraTestProps, TestNodejsFunctionProps, } from '@aws-lambda-powertools/testing-utils/types'; -import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { commonEnvironmentVars } from '../e2e/constants.js'; class TracerTestNodejsFunction extends TestNodejsFunction { diff --git a/packages/tracer/tests/helpers/traceAssertions.ts b/packages/tracer/tests/helpers/traceAssertions.ts index 14b80e5c69..f2a6469abb 100644 --- a/packages/tracer/tests/helpers/traceAssertions.ts +++ b/packages/tracer/tests/helpers/traceAssertions.ts @@ -1,7 +1,7 @@ import { - getFirstSubsegment, type AssertAnnotationParams, type ParsedDocument, + getFirstSubsegment, } from './tracesUtils.js'; export const assertAnnotation = (params: AssertAnnotationParams): void => { @@ -16,8 +16,8 @@ export const assertAnnotation = (params: AssertAnnotationParams): void => { if (!annotations) { fail('annotation is missing'); } - expect(annotations['ColdStart']).toEqual(isColdStart); - expect(annotations['Service']).toEqual(expectedServiceName); + expect(annotations.ColdStart).toEqual(isColdStart); + expect(annotations.Service).toEqual(expectedServiceName); expect(annotations[expectedCustomAnnotationKey]).toEqual( expectedCustomAnnotationValue ); diff --git a/packages/tracer/tests/helpers/tracesUtils.ts b/packages/tracer/tests/helpers/tracesUtils.ts index 8557e60b9b..77d8b4dcb9 100644 --- a/packages/tracer/tests/helpers/tracesUtils.ts +++ b/packages/tracer/tests/helpers/tracesUtils.ts @@ -1,10 +1,10 @@ -import promiseRetry from 'promise-retry'; +import { invokeFunction } from '@aws-lambda-powertools/testing-utils'; import { - XRayClient, BatchGetTracesCommand, GetTraceSummariesCommand, + XRayClient, } from '@aws-sdk/client-xray'; -import { invokeFunction } from '@aws-lambda-powertools/testing-utils'; +import promiseRetry from 'promise-retry'; import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError.js'; interface ParsedDocument { @@ -170,14 +170,14 @@ const getTraces = async ({ for (const trace of sortedTraces) { let retryFlag = false; - let invocationSubsegment; + let invocationSubsegment: ParsedDocument; try { invocationSubsegment = getInvocationSubsegment(trace); } catch (error) { if (error instanceof FunctionSegmentNotDefinedError) { retry( new Error( - `There is no Function subsegment (AWS::Lambda::Function) yet. Retry.` + 'There is no Function subsegment (AWS::Lambda::Function) yet. Retry.' ) ); } else { @@ -205,15 +205,15 @@ const getTraces = async ({ ); } - sortedTraces.forEach((trace) => { - if (trace.Segments?.length != expectedSegmentsCount) { + for (const trace of sortedTraces) { + if (trace.Segments?.length !== expectedSegmentsCount) { retry( new Error( `Expected ${expectedSegmentsCount} segments, got ${trace.Segments?.length} for trace id ${trace.Id}` ) ); } - }); + } return sortedTraces; }, retryOptions); @@ -235,7 +235,7 @@ const getFunctionSegment = (trace: ParsedTrace): ParsedSegment => { const getFirstSubsegment = (segment: ParsedDocument): ParsedDocument => { const subsegments = segment.subsegments; - if (!subsegments || subsegments.length == 0) { + if (!subsegments || subsegments.length === 0) { throw new Error('segment should have subsegments'); } @@ -262,13 +262,13 @@ const splitSegmentsByName = ( const splitSegments: Map = new Map( [...expectedNames, 'other'].map((name) => [name, []]) ); - subsegments.forEach((subsegment) => { + for (const subsegment of subsegments) { const name = expectedNames.indexOf(subsegment.name) !== -1 ? subsegment.name : 'other'; const newSegments = splitSegments.get(name) as ParsedDocument[]; newSegments.push(subsegment); splitSegments.set(name, newSegments); - }); + } return splitSegments; }; diff --git a/packages/tracer/tests/tsconfig.json b/packages/tracer/tests/tsconfig.json index 5654b3e15f..45ba862a85 100644 --- a/packages/tracer/tests/tsconfig.json +++ b/packages/tracer/tests/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": "../", - "noEmit": true - }, - "include": [ - "../src/**/*", - "./**/*", - ] -} \ No newline at end of file + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "../", + "noEmit": true + }, + "include": ["../src/**/*", "./**/*"] +} diff --git a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts index 2e3fb652dd..608ccf54e6 100644 --- a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts +++ b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts @@ -3,7 +3,6 @@ * * @group unit/tracer/all */ - import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariablesService', () => { diff --git a/packages/tracer/tests/unit/ProviderService.test.ts b/packages/tracer/tests/unit/ProviderService.test.ts index 61a0a7c383..f4d0b925aa 100644 --- a/packages/tracer/tests/unit/ProviderService.test.ts +++ b/packages/tracer/tests/unit/ProviderService.test.ts @@ -3,31 +3,31 @@ * * @group unit/tracer/providerservice */ +import { channel } from 'node:diagnostics_channel'; +import http from 'node:http'; +import https from 'node:https'; +import { URL } from 'node:url'; import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { - captureAsyncFunc, + Segment, + Subsegment, captureAWS, captureAWSClient, captureAWSv3Client, + captureAsyncFunc, captureFunc, captureHTTPsGlobal, getNamespace, getSegment, - Segment, setContextMissingStrategy, setDaemonAddress, setLogger, setSegment, - Subsegment, } from 'aws-xray-sdk-core'; -import { channel } from 'node:diagnostics_channel'; -import http from 'node:http'; -import https from 'node:https'; import { ProviderService } from '../../src/provider/ProviderService.js'; import type { HttpSubsegment } from '../../src/types/ProviderService.js'; import { mockFetch } from '../helpers/mockRequests.js'; -import { URL } from 'node:url'; jest.mock('aws-xray-sdk-core', () => ({ ...jest.requireActual('aws-xray-sdk-core'), diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 0119fe7972..9aef06a152 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -3,17 +3,17 @@ * * @group unit/tracer/all */ -import context from '@aws-lambda-powertools/testing-utils/context'; import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { Tracer } from './../../src/index.js'; +import context from '@aws-lambda-powertools/testing-utils/context'; import type { Callback, Context } from 'aws-lambda'; import { Segment, - setContextMissingStrategy, Subsegment, + setContextMissingStrategy, } from 'aws-xray-sdk-core'; -import type { ProviderServiceInterface } from '../../src/types/ProviderService.js'; import type { ConfigServiceInterface } from '../../src/types/ConfigServiceInterface.js'; +import type { ProviderServiceInterface } from '../../src/types/ProviderService.js'; +import { Tracer } from './../../src/index.js'; type CaptureAsyncFuncMock = jest.SpyInstance< unknown, @@ -23,20 +23,19 @@ type CaptureAsyncFuncMock = jest.SpyInstance< parent?: Segment | Subsegment, ] >; -const createCaptureAsyncFuncMock = function ( +const createCaptureAsyncFuncMock = ( provider: ProviderServiceInterface, subsegment?: Subsegment -): CaptureAsyncFuncMock { - return jest +): CaptureAsyncFuncMock => + jest .spyOn(provider, 'captureAsyncFunc') .mockImplementation(async (methodName, callBackFn) => { - if (!subsegment) { - subsegment = new Subsegment(`### ${methodName}`); - } - jest.spyOn(subsegment, 'flush').mockImplementation(() => null); - await callBackFn(subsegment); + const newSubsegment = subsegment + ? subsegment + : new Subsegment(`### ${methodName}`); + jest.spyOn(newSubsegment, 'flush').mockImplementation(() => null); + await callBackFn(newSubsegment); }); -}; jest.spyOn(console, 'log').mockImplementation(() => null); jest.spyOn(console, 'debug').mockImplementation(() => null); @@ -244,7 +243,7 @@ describe('Class: Tracer', () => { test('when AWS_EXECUTION_ENV environment variable is NOT set, tracing is disabled', () => { // Prepare - delete process.env.AWS_EXECUTION_ENV; + process.env.AWS_EXECUTION_ENV = undefined; // Act const tracer = new Tracer(); @@ -445,7 +444,7 @@ describe('Class: Tracer', () => { test('when called when a serviceName has not been set in the constructor or environment variables, it adds the default service name as an annotation', () => { // Prepare - delete process.env.POWERTOOLS_SERVICE_NAME; + process.env.POWERTOOLS_SERVICE_NAME = undefined; const tracer: Tracer = new Tracer(); const putAnnotation = jest .spyOn(tracer, 'putAnnotation') @@ -484,7 +483,7 @@ describe('Class: Tracer', () => { // Assess expect(putMetadataSpy).toBeCalledTimes(0); - delete process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE; + process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = undefined; }); test('when called with data equal to undefined, it does nothing', () => { @@ -546,7 +545,7 @@ describe('Class: Tracer', () => { // Assess expect(addErrorFlagSpy).toBeCalledTimes(1); expect(addErrorSpy).toBeCalledTimes(0); - delete process.env.POWERTOOLS_TRACER_CAPTURE_ERROR; + process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); test('when called with default config, it calls subsegment.addError correctly', () => { @@ -609,7 +608,7 @@ describe('Class: Tracer', () => { test('when called outside of a namespace or without parent segment, and tracing is disabled, it returns a dummy subsegment', () => { // Prepare - delete process.env.AWS_EXECUTION_ENV; // This will disable the tracer, simulating local execution + process.env.AWS_EXECUTION_ENV = undefined; // This will disable the tracer, simulating local execution const tracer: Tracer = new Tracer(); // Act @@ -683,7 +682,7 @@ describe('Class: Tracer', () => { test('when called outside of a namespace or without parent segment, and tracing is disabled, it does nothing', () => { // Prepare - delete process.env.AWS_EXECUTION_ENV; // This will disable the tracer, simulating local execution + process.env.AWS_EXECUTION_ENV = undefined; // This will disable the tracer, simulating local execution const tracer: Tracer = new Tracer(); const setSegmentSpy = jest.spyOn(tracer.provider, 'setSegment'); @@ -852,11 +851,7 @@ describe('Class: Tracer', () => { class Lambda implements LambdaInterface { @tracer.captureLambdaHandler() - public handler( - _event: TEvent, - _context: Context, - _callback: Callback - ): void | Promise { + public handler(_event: TEvent, _context: Context) { return new Promise((resolve, _reject) => resolve({ foo: 'bar', @@ -866,14 +861,12 @@ describe('Class: Tracer', () => { } // Act - await new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await new Lambda().handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); expect(putMetadataSpy).toHaveBeenCalledTimes(0); - delete process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE; + process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = undefined; }); test('when used as decorator while captureResponse is set to false, it does not capture the response as metadata', async () => { @@ -890,11 +883,7 @@ describe('Class: Tracer', () => { class Lambda implements LambdaInterface { @tracer.captureLambdaHandler({ captureResponse: false }) - public handler( - _event: TEvent, - _context: Context, - _callback: Callback - ): void | Promise { + public handler(_event: TEvent, _context: Context) { return new Promise((resolve, _reject) => resolve({ foo: 'bar', @@ -904,9 +893,7 @@ describe('Class: Tracer', () => { } // Act - await new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await new Lambda().handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -927,11 +914,7 @@ describe('Class: Tracer', () => { class Lambda implements LambdaInterface { @tracer.captureLambdaHandler({ captureResponse: true }) - public handler( - _event: TEvent, - _context: Context, - _callback: Callback - ): void | Promise { + public handler(_event: TEvent, _context: Context) { return new Promise((resolve, _reject) => resolve({ foo: 'bar', @@ -941,9 +924,7 @@ describe('Class: Tracer', () => { } // Act - await new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await new Lambda().handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -972,11 +953,7 @@ describe('Class: Tracer', () => { class Lambda implements LambdaInterface { @tracer.captureLambdaHandler() - public handler( - _event: TEvent, - _context: Context, - _callback: Callback - ): void | Promise { + public handler(_event: TEvent, _context: Context) { return new Promise((resolve, _reject) => resolve({ foo: 'bar', @@ -986,9 +963,7 @@ describe('Class: Tracer', () => { } // Act - await new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await new Lambda().handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -1037,7 +1012,7 @@ describe('Class: Tracer', () => { expect(addErrorSpy).toHaveBeenCalledTimes(0); expect.assertions(4); - delete process.env.POWERTOOLS_TRACER_CAPTURE_ERROR; + process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); test('when used as decorator and with standard config, it captures the exception', async () => { @@ -1271,7 +1246,7 @@ describe('Class: Tracer', () => { expect(closeSpy).toHaveBeenCalledTimes(1); expect(logWarningSpy).toHaveBeenNthCalledWith( 1, - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', handlerSubsegment.name, new Error('dummy error') ); @@ -1605,8 +1580,6 @@ describe('Class: Tracer', () => { 'captureAsyncFunc' ); - // Creating custom external decorator - // eslint-disable-next-line func-style function passThrough() { // A decorator that calls the original method. return ( @@ -1614,7 +1587,7 @@ describe('Class: Tracer', () => { _propertyKey: string, descriptor: PropertyDescriptor ) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: we know it's defined because this is a method decorator const originalMethod = descriptor.value!; descriptor.value = function (...args: unknown[]) { return originalMethod.apply(this, [...args]); @@ -1626,7 +1599,7 @@ describe('Class: Tracer', () => { @tracer.captureMethod() @passThrough() public async dummyMethod(): Promise { - return `foo`; + return 'foo'; } public async handler( @@ -1733,7 +1706,7 @@ describe('Class: Tracer', () => { expect(closeSpy).toHaveBeenCalledTimes(1); expect(logWarningSpy).toHaveBeenNthCalledWith( 1, - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', handlerSubsegment.name, new Error('dummy error') ); diff --git a/packages/tracer/tests/unit/middy.test.ts b/packages/tracer/tests/unit/middy.test.ts index 5819e54faa..561a403cc3 100644 --- a/packages/tracer/tests/unit/middy.test.ts +++ b/packages/tracer/tests/unit/middy.test.ts @@ -3,17 +3,17 @@ * * @group unit/tracer/all */ -import { captureLambdaHandler } from '../../src/middleware/middy.js'; +import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; +import context from '@aws-lambda-powertools/testing-utils/context'; import middy from '@middy/core'; -import { Tracer } from './../../src/index.js'; import type { Context, Handler } from 'aws-lambda'; import { Segment, - setContextMissingStrategy, Subsegment, + setContextMissingStrategy, } from 'aws-xray-sdk-core'; -import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; -import context from '@aws-lambda-powertools/testing-utils/context'; +import { captureLambdaHandler } from '../../src/middleware/middy.js'; +import { Tracer } from './../../src/index.js'; jest.spyOn(console, 'debug').mockImplementation(() => null); jest.spyOn(console, 'warn').mockImplementation(() => null); @@ -105,7 +105,7 @@ describe('Middy middleware', () => { // Assess expect(putMetadataSpy).toHaveBeenCalledTimes(0); - delete process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE; + process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = undefined; }); test('when used while captureResponse set to false, it does not capture the response as metadata', async () => { @@ -199,7 +199,7 @@ describe('Middy middleware', () => { expect(addErrorSpy).toHaveBeenCalledTimes(0); expect.assertions(5); - delete process.env.POWERTOOLS_TRACER_CAPTURE_ERROR; + process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); test('when used with standard config, it captures the exception correctly', async () => { @@ -391,7 +391,7 @@ describe('Middy middleware', () => { expect(closeSpy).toHaveBeenCalledTimes(1); expect(logWarningSpy).toHaveBeenNthCalledWith( 1, - `Failed to close or serialize segment %s. We are catching the error but data might be lost.`, + 'Failed to close or serialize segment %s. We are catching the error but data might be lost.', handlerSubsegment.name, new Error('dummy error') ); diff --git a/packages/tracer/tsconfig.esm.json b/packages/tracer/tsconfig.esm.json index 123291b0cf..82486b64fa 100644 --- a/packages/tracer/tsconfig.esm.json +++ b/packages/tracer/tsconfig.esm.json @@ -6,7 +6,5 @@ "rootDir": "./src", "tsBuildInfoFile": ".tsbuildinfo/esm.json" }, - "include": [ - "./src/**/*" - ] -} \ No newline at end of file + "include": ["./src/**/*"] +} diff --git a/packages/tracer/tsconfig.json b/packages/tracer/tsconfig.json index f216927295..0f0cc593ac 100644 --- a/packages/tracer/tsconfig.json +++ b/packages/tracer/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./lib/cjs", - "rootDir": "./src", - "tsBuildInfoFile": ".tsbuildinfo/cjs.json" - }, - "include": [ - "./src/**/*" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib/cjs", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" + }, + "include": ["./src/**/*"] +} diff --git a/packages/tracer/typedoc.json b/packages/tracer/typedoc.json index 8ead571a7f..ecfe51c09b 100644 --- a/packages/tracer/typedoc.json +++ b/packages/tracer/typedoc.json @@ -2,4 +2,4 @@ "extends": ["../../typedoc.base.json"], "entryPoints": ["./src/index.ts", "./src/types/index.ts"], "readme": "README.md" -} \ No newline at end of file +} From abf42f11aab1bb4855ff59213e56eab3c471402b Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 23 Jul 2024 13:47:49 +0200 Subject: [PATCH 2/3] chore: address Sonar findings --- ...allFeatures.decorator.test.functionCode.ts | 6 +- packages/tracer/tests/unit/Tracer.test.ts | 245 +++++------------- 2 files changed, 66 insertions(+), 185 deletions(-) diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts index 5719b37fc1..8e9ed4aba1 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts @@ -41,7 +41,7 @@ export class MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): unknown | Promise { + ): unknown { tracer.putAnnotation(customAnnotationKey, customAnnotationValue); tracer.putMetadata(customMetadataKey, customMetadataValue); @@ -84,7 +84,7 @@ class MyFunctionWithDecorator extends MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): unknown | Promise { + ): unknown { return super.handler(event, _context, _callback); } @@ -103,7 +103,7 @@ class MyFunctionWithDecoratorCaptureResponseFalse extends MyFunctionBase { event: CustomEvent, _context: Context, _callback: Callback - ): unknown | Promise { + ): unknown { return super.handler(event, _context, _callback); } diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 9aef06a152..39e5f1b18e 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -5,7 +5,7 @@ */ import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import context from '@aws-lambda-powertools/testing-utils/context'; -import type { Callback, Context } from 'aws-lambda'; +import type { Context } from 'aws-lambda'; import { Segment, Subsegment, @@ -14,6 +14,7 @@ import { import type { ConfigServiceInterface } from '../../src/types/ConfigServiceInterface.js'; import type { ProviderServiceInterface } from '../../src/types/ProviderService.js'; import { Tracer } from './../../src/index.js'; +import type { CaptureLambdaHandlerOptions } from './../../src/types/index.js'; type CaptureAsyncFuncMock = jest.SpyInstance< unknown, @@ -30,11 +31,9 @@ const createCaptureAsyncFuncMock = ( jest .spyOn(provider, 'captureAsyncFunc') .mockImplementation(async (methodName, callBackFn) => { - const newSubsegment = subsegment - ? subsegment - : new Subsegment(`### ${methodName}`); + const newSubsegment = subsegment || new Subsegment(`### ${methodName}`); jest.spyOn(newSubsegment, 'flush').mockImplementation(() => null); - await callBackFn(newSubsegment); + return await callBackFn(newSubsegment); }); jest.spyOn(console, 'log').mockImplementation(() => null); @@ -805,6 +804,28 @@ describe('Class: Tracer', () => { }); describe('Method: captureLambdaHandler', () => { + const getLambdaClass = ( + tracer: Tracer, + options?: { + shouldThrow?: boolean; + tracerOptions?: CaptureLambdaHandlerOptions; + } + ) => { + class Lambda implements LambdaInterface { + @tracer.captureLambdaHandler(options?.tracerOptions) + public handler(_event: TEvent, _context: Context) { + if (options?.shouldThrow) throw new Error('An error has occurred'); + return new Promise((resolve, _reject) => + resolve({ + foo: 'bar', + } as unknown as TResult) + ); + } + } + + return new Lambda(); + }; + test('when used as decorator while tracing is disabled, it does nothing', async () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); @@ -817,23 +838,10 @@ describe('Class: Tracer', () => { tracer.provider, 'captureAsyncFunc' ); - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler( - _event: unknown, - _context: Context, - callback: Callback - ): void { - callback(null, { - foo: 'bar', - }); - } - } + const lambda = getLambdaClass(tracer); // Act - new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(0); @@ -848,20 +856,10 @@ describe('Class: Tracer', () => { 'captureAsyncFunc' ); const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler(_event: TEvent, _context: Context) { - return new Promise((resolve, _reject) => - resolve({ - foo: 'bar', - } as unknown as TResult) - ); - } - } + const lambda = getLambdaClass(tracer); // Act - await new Lambda().handler(event, context); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -880,20 +878,12 @@ describe('Class: Tracer', () => { tracer, 'addResponseAsMetadata' ); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler({ captureResponse: false }) - public handler(_event: TEvent, _context: Context) { - return new Promise((resolve, _reject) => - resolve({ - foo: 'bar', - } as unknown as TResult) - ); - } - } + const lambda = getLambdaClass(tracer, { + tracerOptions: { captureResponse: false }, + }); // Act - await new Lambda().handler(event, context); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -911,20 +901,10 @@ describe('Class: Tracer', () => { tracer, 'addResponseAsMetadata' ); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler({ captureResponse: true }) - public handler(_event: TEvent, _context: Context) { - return new Promise((resolve, _reject) => - resolve({ - foo: 'bar', - } as unknown as TResult) - ); - } - } + const lambda = getLambdaClass(tracer); // Act - await new Lambda().handler(event, context); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -950,20 +930,10 @@ describe('Class: Tracer', () => { tracer, 'addResponseAsMetadata' ); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler(_event: TEvent, _context: Context) { - return new Promise((resolve, _reject) => - resolve({ - foo: 'bar', - } as unknown as TResult) - ); - } - } + const lambda = getLambdaClass(tracer); // Act - await new Lambda().handler(event, context); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -990,23 +960,10 @@ describe('Class: Tracer', () => { jest.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); const addErrorFlagSpy = jest.spyOn(newSubsegment, 'addErrorFlag'); const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler( - _event: unknown, - _context: Context, - _callback: Callback - ): void { - throw new Error('Exception thrown!'); - } - } - const lambda = new Lambda(); + const lambda = getLambdaClass(tracer, { shouldThrow: true }); // Act & Assess - expect( - lambda.handler({}, context, () => console.log('Lambda invoked!')) - ).rejects.toThrowError(Error); + expect(lambda.handler({}, context)).rejects.toThrow(Error); expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); expect(addErrorFlagSpy).toHaveBeenCalledTimes(1); expect(addErrorSpy).toHaveBeenCalledTimes(0); @@ -1027,23 +984,10 @@ describe('Class: Tracer', () => { jest.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); const addErrorFlagSpy = jest.spyOn(newSubsegment, 'addErrorFlag'); const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler( - _event: unknown, - _context: Context, - _callback: Callback - ): void { - throw new Error('Exception thrown!2'); - } - } + const lambda = getLambdaClass(tracer, { shouldThrow: true }); // Act & Assess - const lambda = new Lambda(); - expect( - lambda.handler({}, context, () => console.log('Lambda invoked!')) - ).rejects.toThrowError(Error); + expect(lambda.handler({}, context)).rejects.toThrow(Error); expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); expect(addErrorAsMetadataSpy).toHaveBeenCalledTimes(1); expect(addErrorAsMetadataSpy).toHaveBeenCalledWith(expect.any(Error)); @@ -1057,22 +1001,10 @@ describe('Class: Tracer', () => { const tracer: Tracer = new Tracer(); const captureAsyncFuncSpy = createCaptureAsyncFuncMock(tracer.provider); const annotateColdStartSpy = jest.spyOn(tracer, 'annotateColdStart'); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler( - _event: unknown, - _context: Context, - callback: Callback<{ foo: string }> - ): void { - callback(null, { foo: 'bar' }); - } - } + const lambda = getLambdaClass(tracer); // Act - new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -1091,24 +1023,10 @@ describe('Class: Tracer', () => { tracer, 'addServiceNameAnnotation' ); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public handler( - _event: unknown, - _context: Context, - callback: Callback<{ foo: string }> - ): void { - callback(null, { - foo: 'bar', - }); - } - } + const lambda = getLambdaClass(tracer); // Act - new Lambda().handler(event, context, () => - console.log('Lambda invoked!') - ); + await lambda.handler(event, context); // Assess expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); @@ -1120,39 +1038,6 @@ describe('Class: Tracer', () => { expect(addServiceNameAnnotationSpy).toHaveBeenCalledTimes(1); }); - test('when used as decorator and when calling the handler, it has access to member variables', async () => { - // Prepare - const tracer: Tracer = new Tracer(); - const newSubsegment: Segment | Subsegment | undefined = new Subsegment( - '### dummyMethod' - ); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); - setContextMissingStrategy(() => null); - - class Lambda implements LambdaInterface { - private readonly memberVariable: string; - - public constructor(memberVariable: string) { - this.memberVariable = memberVariable; - } - - @tracer.captureLambdaHandler() - public async handler( - _event: unknown, - _context: Context - ): Promise { - return `memberVariable:${this.memberVariable}`; - } - } - - // Act / Assess - const lambda = new Lambda('someValue'); - const handler = lambda.handler.bind(lambda); - expect(await handler({}, context)).toEqual('memberVariable:someValue'); - }); - test('when used as decorator on an async method, the method is awaited correctly', async () => { // Prepare const tracer: Tracer = new Tracer(); @@ -1170,35 +1055,40 @@ describe('Class: Tracer', () => { createCaptureAsyncFuncMock(tracer.provider, newSubsegment); class Lambda implements LambdaInterface { - public async dummyMethod(): Promise { - return; + private memberVariable: string; + + public constructor(memberVariable: string) { + this.memberVariable = memberVariable; + } + + public async dummyMethod(): Promise { + return this.memberVariable; } @tracer.captureLambdaHandler() public async handler( _event: unknown, _context: Context - ): Promise { - await this.dummyMethod(); + ): Promise { + const result = await this.dummyMethod(); this.otherDummyMethod(); - return; + return this.memberVariable; } public otherDummyMethod(): void { return; } } + const lambda = new Lambda('someValue'); + const otherDummyMethodSpy = jest.spyOn(lambda, 'otherDummyMethod'); // Act - const lambda = new Lambda(); - const otherDummyMethodSpy = jest - .spyOn(lambda, 'otherDummyMethod') - .mockImplementation(); const handler = lambda.handler.bind(lambda); - await handler({}, context); + const result = await handler({}, context); // Assess + expect(result).toEqual('someValue'); // Here we assert that the otherDummyMethodSpy method is called before the cleanup logic (inside the finally of decorator) // that should always be called after the handler has returned. If otherDummyMethodSpy is called after it means the // decorator is NOT awaiting the handler which would cause the test to fail. @@ -1228,19 +1118,10 @@ describe('Class: Tracer', () => { .mockImplementation(() => { throw new Error('dummy error'); }); - - class Lambda implements LambdaInterface { - @tracer.captureLambdaHandler() - public async handler( - _event: unknown, - _context: Context - ): Promise { - return 'foo bar'; - } - } + const lambda = getLambdaClass(tracer); // Act - await new Lambda().handler(event, context); + await lambda.handler(event, context); // Assess expect(closeSpy).toHaveBeenCalledTimes(1); From 350941c1f9791ba99ebfa171e2320adb93887842 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 23 Jul 2024 14:06:14 +0200 Subject: [PATCH 3/3] chore: address Sonar findings --- packages/tracer/tests/unit/Tracer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 39e5f1b18e..af1924bc3b 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -1073,7 +1073,7 @@ describe('Class: Tracer', () => { const result = await this.dummyMethod(); this.otherDummyMethod(); - return this.memberVariable; + return result; } public otherDummyMethod(): void {