diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml index 7ed667a411..577a66f53d 100644 --- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml +++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml @@ -47,6 +47,7 @@ jobs: "packages/idempotency", "packages/jmespath", "packages/logger", + "packages/tracer", ] fail-fast: false steps: @@ -89,14 +90,12 @@ jobs: nodeVersion: ${{ matrix.version }} - name: Run linting run: | - npm run lint -w -w packages/tracer \ - -w packages/metrics \ + npm run lint -w packages/metrics \ -w packages/parameters \ -w packages/parser - name: Run unit tests run: | - npm t -w packages/tracer \ - -w packages/metrics \ + npm t -w packages/metrics \ -w packages/parameters \ -w packages/parser check-examples: diff --git a/.husky/pre-push b/.husky/pre-push index ebb286f3b0..c3b8dee21d 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,6 +1,5 @@ npm t \ -w packages/metrics \ - -w packages/tracer \ -w packages/parameters \ -w packages/parser diff --git a/packages/logger/tests/unit/initializeLogger.test.ts b/packages/logger/tests/unit/initializeLogger.test.ts index 28bc5aa942..cc763d2730 100644 --- a/packages/logger/tests/unit/initializeLogger.test.ts +++ b/packages/logger/tests/unit/initializeLogger.test.ts @@ -152,7 +152,7 @@ describe('Log levels', () => { level: 'INFO', message: 'Hello, world!', sampling_rate: 0, - service: 'service_undefined', + service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-abcdef12-3456abcdef123456abcdef12', }, diff --git a/packages/testing/src/setupEnv.ts b/packages/testing/src/setupEnv.ts index f0d7b06c47..abc8a63691 100644 --- a/packages/testing/src/setupEnv.ts +++ b/packages/testing/src/setupEnv.ts @@ -123,3 +123,5 @@ if ( process.env.AWS_REGION = 'eu-west-1'; } process.env._HANDLER = 'index.handler'; +process.env.POWERTOOLS_SERVICE_NAME = 'hello-world'; +process.env.AWS_XRAY_LOGGING_LEVEL = 'silent'; diff --git a/packages/tracer/jest.config.cjs b/packages/tracer/jest.config.cjs deleted file mode 100644 index 4932f36338..0000000000 --- a/packages/tracer/jest.config.cjs +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - displayName: { - name: 'Powertools for AWS Lambda (TypeScript) utility: TRACER', - color: 'white', - }, - runner: 'groups', - preset: 'ts-jest', - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - transform: { - '^.+\\.ts?$': 'ts-jest', - }, - moduleFileExtensions: ['js', 'ts'], - collectCoverageFrom: ['**/src/**/*.ts', '!**/node_modules/**'], - testMatch: ['**/?(*.)+(spec|test).ts'], - roots: ['/src', '/tests'], - testPathIgnorePatterns: ['/node_modules/'], - testEnvironment: 'node', - coveragePathIgnorePatterns: ['/node_modules/', '/types/'], - coverageThreshold: { - global: { - statements: 100, - branches: 100, - functions: 100, - lines: 100, - }, - }, - coverageReporters: ['json-summary', 'text', 'lcov'], - setupFiles: ['/tests/helpers/populateEnvironmentVariables.ts'], -}; diff --git a/packages/tracer/package.json b/packages/tracer/package.json index c590b62a77..6ba287896d 100644 --- a/packages/tracer/package.json +++ b/packages/tracer/package.json @@ -10,13 +10,14 @@ "access": "public" }, "scripts": { - "test": "npm run test:unit", - "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", - "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", - "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", - "test:e2e": "jest --group=e2e", - "watch": "jest --watch", + "test": "vitest --run tests/unit", + "test:unit": "vitest --run tests/unit", + "test:unit:coverage": "vitest --run tests/unit --coverage.enabled --coverage.thresholds.100 --coverage.include='src/**'", + "test:unit:types": "echo 'Not Implemented'", + "test:unit:watch": "vitest tests/unit", + "test:e2e:nodejs18x": "RUNTIME=nodejs18x vitest --run tests/e2e", + "test:e2e:nodejs20x": "RUNTIME=nodejs20x vitest --run tests/e2e", + "test:e2e": "vitest --run tests/e2e", "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", diff --git a/packages/tracer/src/Tracer.ts b/packages/tracer/src/Tracer.ts index b52e963658..b5310aa8e3 100644 --- a/packages/tracer/src/Tracer.ts +++ b/packages/tracer/src/Tracer.ts @@ -321,7 +321,7 @@ class Tracer extends Utility implements TracerInterface { * @deprecated Use {@link captureAWSv3Client} instead. * @param service - AWS SDK v2 client */ - public captureAWSClient(service: T): T { + /* v8 ignore start */ public captureAWSClient(service: T): T { if (!this.isTracingEnabled()) return service; try { @@ -338,7 +338,7 @@ class Tracer extends Utility implements TracerInterface { throw error; } } - } + } /* v8 ignore stop */ /** * Patch an AWS SDK v3 client and create traces when your application makes calls to that AWS service. diff --git a/packages/tracer/tests/e2e/decorator.test.ts b/packages/tracer/tests/e2e/decorator.test.ts index 273464000b..8a87282431 100644 --- a/packages/tracer/tests/e2e/decorator.test.ts +++ b/packages/tracer/tests/e2e/decorator.test.ts @@ -1,14 +1,10 @@ -/** - * Test tracer when using the decorator instrumentation - * - * @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 { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { getTraces } from '@aws-lambda-powertools/testing-utils/utils/xray-traces'; import type { EnrichedXRayTraceDocumentParsed } from 'packages/testing/lib/cjs/types.js'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { invokeAllTestCases } from '../helpers/invokeAllTests.js'; import { RESOURCE_NAME_PREFIX, diff --git a/packages/tracer/tests/e2e/manual.test.ts b/packages/tracer/tests/e2e/manual.test.ts index 62cb605152..80fa766be1 100644 --- a/packages/tracer/tests/e2e/manual.test.ts +++ b/packages/tracer/tests/e2e/manual.test.ts @@ -1,14 +1,10 @@ -/** - * Test tracer when instrumenting the lambda function manually - * - * @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 { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { getTraces } from '@aws-lambda-powertools/testing-utils/utils/xray-traces'; import type { EnrichedXRayTraceDocumentParsed } from 'packages/testing/lib/cjs/types.js'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { invokeAllTestCases } from '../helpers/invokeAllTests.js'; import { RESOURCE_NAME_PREFIX, diff --git a/packages/tracer/tests/e2e/middy.test.ts b/packages/tracer/tests/e2e/middy.test.ts index 408e188578..0a33d4377e 100644 --- a/packages/tracer/tests/e2e/middy.test.ts +++ b/packages/tracer/tests/e2e/middy.test.ts @@ -1,14 +1,10 @@ -/** - * Test tracer when using the Middy.js instrumentation - * - * @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 { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { getTraces } from '@aws-lambda-powertools/testing-utils/utils/xray-traces'; import type { EnrichedXRayTraceDocumentParsed } from 'packages/testing/lib/cjs/types.js'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { invokeAllTestCases } from '../helpers/invokeAllTests.js'; import { RESOURCE_NAME_PREFIX, diff --git a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts b/packages/tracer/tests/helpers/populateEnvironmentVariables.ts deleted file mode 100644 index 3d3087b9e3..0000000000 --- a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Reserved variables -process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12'; -process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function'; -process.env.AWS_EXECUTION_ENV = 'nodejs20.x'; -process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; -if ( - process.env.AWS_REGION === undefined && - process.env.CDK_DEFAULT_REGION === undefined -) { - process.env.AWS_REGION = 'eu-west-1'; -} -process.env._HANDLER = 'index.handler'; -process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = '1'; - -// Powertools for AWS Lambda (TypeScript) variables -process.env.POWERTOOLS_SERVICE_NAME = 'hello-world'; -process.env.AWS_XRAY_LOGGING_LEVEL = 'silent'; diff --git a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts index 608ccf54e6..1a6b137465 100644 --- a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts +++ b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts @@ -1,15 +1,11 @@ -/** - * Test EnvironmentVariablesService class - * - * @group unit/tracer/all - */ +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariablesService', () => { const ENVIRONMENT_VARIABLES = process.env; beforeEach(() => { - jest.resetModules(); + vi.resetModules(); process.env = { ...ENVIRONMENT_VARIABLES }; }); @@ -18,7 +14,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getTracingEnabled', () => { - test('It returns the value of the environment variable POWERTOOLS_TRACE_ENABLED', () => { + it('returns the value of the environment variable POWERTOOLS_TRACE_ENABLED', () => { // Prepare process.env.POWERTOOLS_TRACE_ENABLED = 'false'; const service = new EnvironmentVariablesService(); @@ -32,7 +28,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getTracingCaptureResponse', () => { - test('It returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE', () => { + it('returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; const service = new EnvironmentVariablesService(); @@ -46,7 +42,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getTracingCaptureError', () => { - test('It returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_ERROR', () => { + it('returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_ERROR', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; const service = new EnvironmentVariablesService(); @@ -60,7 +56,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getCaptureHTTPsRequests', () => { - test('It returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS', () => { + it('returns the value of the environment variable POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = 'false'; const service = new EnvironmentVariablesService(); @@ -74,7 +70,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getSamLocal', () => { - test('It returns the value of the environment variable AWS_SAM_LOCAL', () => { + it('returns the value of the environment variable AWS_SAM_LOCAL', () => { // Prepare process.env.AWS_SAM_LOCAL = 'true'; const service = new EnvironmentVariablesService(); @@ -88,7 +84,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: getAwsExecutionEnv', () => { - test('It returns the value of the environment variable AWS_EXECUTION_ENV', () => { + it('returns the value of the environment variable AWS_EXECUTION_ENV', () => { // Prepare process.env.AWS_EXECUTION_ENV = 'nodejs20.x'; const service = new EnvironmentVariablesService(); diff --git a/packages/tracer/tests/unit/ProviderService.test.ts b/packages/tracer/tests/unit/ProviderService.test.ts index 65688774a1..dc6fd36064 100644 --- a/packages/tracer/tests/unit/ProviderService.test.ts +++ b/packages/tracer/tests/unit/ProviderService.test.ts @@ -1,62 +1,54 @@ -/** - * Test ProviderService class - * - * @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 { - Segment, - Subsegment, - captureAWS, - captureAWSClient, - captureAWSv3Client, - captureAsyncFunc, - captureFunc, - captureHTTPsGlobal, - getNamespace, - getSegment, - setContextMissingStrategy, - setDaemonAddress, - setLogger, - setSegment, -} from 'aws-xray-sdk-core'; +import xraySDK from 'aws-xray-sdk-core'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { ProviderService } from '../../src/provider/ProviderService.js'; import type { HttpSubsegment } from '../../src/types/ProviderService.js'; import { mockFetch } from '../helpers/mockRequests.js'; -jest.mock('aws-xray-sdk-core', () => ({ - ...jest.requireActual('aws-xray-sdk-core'), - captureAWS: jest.fn(), - captureAWSClient: jest.fn(), - captureAWSv3Client: jest.fn(), - captureAsyncFunc: jest.fn(), - captureHTTPsGlobal: jest.fn(), - captureFunc: jest.fn(), - getNamespace: jest.fn(), - getSegment: jest.fn(), - setContextMissingStrategy: jest.fn(), - setDaemonAddress: jest.fn(), - setLogger: jest.fn(), - setSegment: jest.fn(), +const { Segment, Subsegment } = xraySDK; +const mocks = vi.hoisted(() => ({ + captureAWS: vi.fn(), + captureAWSClient: vi.fn(), + captureAWSv3Client: vi.fn(), + captureAsyncFunc: vi.fn(), + captureHTTPsGlobal: vi.fn(), + captureFunc: vi.fn(), + getNamespace: vi.fn(), + getSegment: vi.fn(), + setContextMissingStrategy: vi.fn(), + setDaemonAddress: vi.fn(), + setLogger: vi.fn(), + setSegment: vi.fn(), })); -jest.mock('@aws-lambda-powertools/commons', () => ({ - ...jest.requireActual('@aws-lambda-powertools/commons'), - addUserAgentMiddleware: jest.fn(), +vi.mock('aws-xray-sdk-core', async (importOriginal) => ({ + default: { + ...( + await importOriginal<{ + default: typeof import('aws-xray-sdk-core'); + }>() + ).default, + ...mocks, + }, +})); + +vi.mock('@aws-lambda-powertools/commons', async (importOriginal) => ({ + ...(await importOriginal()), + addUserAgentMiddleware: vi.fn(), })); describe('Class: ProviderService', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Method: captureAWS', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct underlying function with proper arguments', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -64,13 +56,13 @@ describe('Class: ProviderService', () => { provider.captureAWS({}); // Assess - expect(captureAWS).toHaveBeenCalledTimes(1); - expect(captureAWS).toHaveBeenCalledWith({}); + expect(mocks.captureAWS).toHaveBeenCalledTimes(1); + expect(mocks.captureAWS).toHaveBeenCalledWith({}); }); }); describe('Method: captureAWSClient', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct underlying function with proper arguments', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -78,13 +70,13 @@ describe('Class: ProviderService', () => { provider.captureAWSClient({}); // Assess - expect(captureAWSClient).toHaveBeenCalledTimes(1); - expect(captureAWSClient).toHaveBeenCalledWith({}); + expect(mocks.captureAWSClient).toHaveBeenCalledTimes(1); + expect(mocks.captureAWSClient).toHaveBeenCalledWith({}); }); }); describe('Method: captureAWSv3Client', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct underlying function with proper arguments', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -92,11 +84,11 @@ describe('Class: ProviderService', () => { provider.captureAWSv3Client({}); // Assess - expect(captureAWSv3Client).toHaveBeenCalledTimes(1); - expect(captureAWSv3Client).toHaveBeenCalledWith({}); + expect(mocks.captureAWSv3Client).toHaveBeenCalledTimes(1); + expect(mocks.captureAWSv3Client).toHaveBeenCalledWith({}); }); - test('when called, it adds the correct user agent middleware', () => { + it('adds the correct user agent middleware', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -114,7 +106,7 @@ describe('Class: ProviderService', () => { }); describe('Method: captureAsyncFunc', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct underlying function function', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -122,8 +114,8 @@ describe('Class: ProviderService', () => { provider.captureAsyncFunc('my-func', () => true); // Assess - expect(captureAsyncFunc).toHaveBeenCalledTimes(1); - expect(captureAsyncFunc).toHaveBeenCalledWith( + expect(mocks.captureAsyncFunc).toHaveBeenCalledTimes(1); + expect(mocks.captureAsyncFunc).toHaveBeenCalledWith( 'my-func', expect.anything() ); @@ -131,7 +123,7 @@ describe('Class: ProviderService', () => { }); describe('Method: captureHTTPsGlobal', () => { - test('when called, it forwards the correct parameter and calls the correct function, twice', () => { + it('calls the correct underlying function with proper arguments', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -139,14 +131,14 @@ describe('Class: ProviderService', () => { provider.captureHTTPsGlobal(); // Assess - expect(captureHTTPsGlobal).toHaveBeenCalledTimes(2); - expect(captureHTTPsGlobal).toHaveBeenNthCalledWith(1, http); - expect(captureHTTPsGlobal).toHaveBeenNthCalledWith(2, https); + expect(mocks.captureHTTPsGlobal).toHaveBeenCalledTimes(2); + expect(mocks.captureHTTPsGlobal).toHaveBeenNthCalledWith(1, http); + expect(mocks.captureHTTPsGlobal).toHaveBeenNthCalledWith(2, https); }); }); describe('Method: captureFunc', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct underlying function with proper arguments', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -154,27 +146,16 @@ describe('Class: ProviderService', () => { provider.captureFunc('my-func', () => true); // Assess - expect(captureFunc).toHaveBeenCalledTimes(1); - expect(captureFunc).toHaveBeenCalledWith('my-func', expect.anything()); - }); - }); - - describe('Method: captureFunc', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { - // Prepare - const provider: ProviderService = new ProviderService(); - - // Act - provider.captureFunc('my-func', () => true); - - // Assess - expect(captureFunc).toHaveBeenCalledTimes(1); - expect(captureFunc).toHaveBeenCalledWith('my-func', expect.anything()); + expect(mocks.captureFunc).toHaveBeenCalledTimes(1); + expect(mocks.captureFunc).toHaveBeenCalledWith( + 'my-func', + expect.anything() + ); }); }); describe('Method: getNamespace', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct sdk function', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -182,27 +163,13 @@ describe('Class: ProviderService', () => { provider.getNamespace(); // Assess - expect(getNamespace).toHaveBeenCalledTimes(1); - expect(getNamespace).toHaveBeenCalledWith(); - }); - }); - - describe('Method: getSegment', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { - // Prepare - const provider: ProviderService = new ProviderService(); - - // Act - provider.getSegment(); - - // Assess - expect(getSegment).toHaveBeenCalledTimes(1); - expect(getSegment).toHaveBeenCalledWith(); + expect(mocks.getNamespace).toHaveBeenCalledTimes(1); + expect(mocks.getNamespace).toHaveBeenCalledWith(); }); }); describe('Method: setContextMissingStrategy', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct sdk function', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -210,13 +177,13 @@ describe('Class: ProviderService', () => { provider.setContextMissingStrategy('LOG_ERROR'); // Assess - expect(setContextMissingStrategy).toHaveBeenCalledTimes(1); - expect(setContextMissingStrategy).toHaveBeenCalledWith('LOG_ERROR'); + expect(mocks.setContextMissingStrategy).toHaveBeenCalledTimes(1); + expect(mocks.setContextMissingStrategy).toHaveBeenCalledWith('LOG_ERROR'); }); }); describe('Method: setDaemonAddress', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct sdk function', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -224,13 +191,15 @@ describe('Class: ProviderService', () => { provider.setDaemonAddress('http://localhost:8000'); // Assess - expect(setDaemonAddress).toHaveBeenCalledTimes(1); - expect(setDaemonAddress).toHaveBeenCalledWith('http://localhost:8000'); + expect(mocks.setDaemonAddress).toHaveBeenCalledTimes(1); + expect(mocks.setDaemonAddress).toHaveBeenCalledWith( + 'http://localhost:8000' + ); }); }); describe('Method: setLogger', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct sdk function', () => { // Prepare const provider: ProviderService = new ProviderService(); @@ -238,30 +207,31 @@ describe('Class: ProviderService', () => { provider.setLogger({}); // Assess - expect(setLogger).toHaveBeenCalledTimes(1); - expect(setLogger).toHaveBeenCalledWith({}); + expect(mocks.setLogger).toHaveBeenCalledTimes(1); + expect(mocks.setLogger).toHaveBeenCalledWith({}); }); }); describe('Method: setSegment', () => { - test('when called, it forwards the correct parameter, and call the correct function', () => { + it('calls the correct sdk function', () => { // Prepare const provider: ProviderService = new ProviderService(); + const subsegment = new Subsegment('## foo-bar'); // Act - provider.setSegment({ name: '## foo-bar' } as unknown as Subsegment); + provider.setSegment(subsegment); // Assess - expect(setSegment).toHaveBeenCalledTimes(1); - expect(setSegment).toHaveBeenCalledWith({ name: '## foo-bar' }); + expect(mocks.setSegment).toHaveBeenCalledTimes(1); + expect(mocks.setSegment).toHaveBeenCalledWith(subsegment); }); }); describe('Method: putAnnotation', () => { - test('when called and there is no segment, it logs a warning and does not throw', () => { + it('logs a warning and does not throw when there is no active segment', () => { // Prepare const provider: ProviderService = new ProviderService(); - const logSpy = jest.spyOn(console, 'warn').mockImplementation(); + const logSpy = vi.spyOn(console, 'warn'); // Act provider.putAnnotation('foo', 'bar'); @@ -273,15 +243,15 @@ describe('Class: ProviderService', () => { ); }); - test('when called and the current segment is not a subsegment, it logs a warning and does not annotate the segment', () => { + it('logs a warning and does not annotate the segment when called on a segment', () => { // Prepare const provider: ProviderService = new ProviderService(); const facade = new Segment('facade'); - const logWarningSpy = jest.spyOn(console, 'warn').mockImplementation(); - jest - .spyOn(provider, 'getSegment') - .mockImplementation(() => new Segment('facade')); - const addAnnotationSpy = jest.spyOn(facade, 'addAnnotation'); + const logWarningSpy = vi.spyOn(console, 'warn'); + vi.spyOn(provider, 'getSegment').mockImplementation( + () => new Segment('facade') + ); + const addAnnotationSpy = vi.spyOn(facade, 'addAnnotation'); // Act provider.putAnnotation('foo', 'bar'); @@ -294,12 +264,12 @@ describe('Class: ProviderService', () => { expect(addAnnotationSpy).toHaveBeenCalledTimes(0); }); - test('when called and the current segment is a subsegment, it annotates it', () => { + it('annotates the currently active segment', () => { // Prepare const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); - jest.spyOn(provider, 'getSegment').mockImplementation(() => segment); - const segmentSpy = jest.spyOn(segment, 'addAnnotation'); + vi.spyOn(provider, 'getSegment').mockImplementation(() => segment); + const segmentSpy = vi.spyOn(segment, 'addAnnotation'); // Act provider.putAnnotation('foo', 'bar'); @@ -311,10 +281,10 @@ describe('Class: ProviderService', () => { }); describe('Method: putMetadata', () => { - test('when called and there is no segment, it logs a warning and does not throw', () => { + it('logs a warning and does not throw when called and there is no segment', () => { // Prepare const provider: ProviderService = new ProviderService(); - const logWarningSpy = jest.spyOn(console, 'warn').mockImplementation(); + const logWarningSpy = vi.spyOn(console, 'warn'); // Act provider.putMetadata('foo', 'bar'); @@ -326,15 +296,15 @@ describe('Class: ProviderService', () => { ); }); - test('when called and the current segment is not a subsegment, it logs a warning and does not annotate the segment', () => { + it('logs a warning and does not annotate the segment when called on a segment', () => { // Prepare const provider: ProviderService = new ProviderService(); const facade = new Segment('facade'); - const logSpy = jest.spyOn(console, 'warn').mockImplementation(); - jest - .spyOn(provider, 'getSegment') - .mockImplementation(() => new Segment('facade')); - const facadeSpy = jest.spyOn(facade, 'addMetadata'); + const logSpy = vi.spyOn(console, 'warn'); + vi.spyOn(provider, 'getSegment').mockImplementation( + () => new Segment('facade') + ); + const facadeSpy = vi.spyOn(facade, 'addMetadata'); // Act provider.putMetadata('foo', 'bar'); @@ -347,12 +317,12 @@ describe('Class: ProviderService', () => { expect(facadeSpy).toHaveBeenCalledTimes(0); }); - test('when called and the current segment is a subsegment, it adds the metadata', () => { + it('adds the metadata on the currently active subsegment', () => { // Prepare const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); - jest.spyOn(provider, 'getSegment').mockImplementation(() => segment); - const segmentSpy = jest.spyOn(segment, 'addMetadata'); + vi.spyOn(provider, 'getSegment').mockImplementation(() => segment); + const segmentSpy = vi.spyOn(segment, 'addMetadata'); // Act provider.putMetadata('foo', 'bar', 'baz'); @@ -382,16 +352,15 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -425,16 +394,15 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -465,17 +433,16 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest.spyOn(subsegment, 'addThrottleFlag'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(subsegment, 'addThrottleFlag'); + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -502,17 +469,16 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest.spyOn(subsegment, 'addErrorFlag'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(subsegment, 'addErrorFlag'); + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -539,17 +505,16 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest.spyOn(subsegment, 'addFaultFlag'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(subsegment, 'addFaultFlag'); + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -575,9 +540,9 @@ describe('Class: ProviderService', () => { // Prepare const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); - jest.spyOn(segment, 'addNewSubsegment'); - jest.spyOn(provider, 'getSegment').mockImplementation(() => segment); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(segment, 'addNewSubsegment'); + vi.spyOn(provider, 'getSegment').mockImplementation(() => segment); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -593,16 +558,15 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); @@ -627,17 +591,16 @@ describe('Class: ProviderService', () => { const provider: ProviderService = new ProviderService(); const segment = new Subsegment('## dummySegment'); const subsegment = segment.addNewSubsegment('aws.amazon.com'); - jest.spyOn(subsegment, 'addError'); - jest - .spyOn(segment, 'addNewSubsegment') - .mockImplementationOnce(() => subsegment); - jest - .spyOn(provider, 'getSegment') + vi.spyOn(subsegment, 'addError'); + vi.spyOn(segment, 'addNewSubsegment').mockImplementationOnce( + () => subsegment + ); + vi.spyOn(provider, 'getSegment') .mockImplementationOnce(() => segment) .mockImplementationOnce(() => subsegment) .mockImplementationOnce(() => subsegment); - jest.spyOn(subsegment, 'close'); - jest.spyOn(provider, 'setSegment'); + vi.spyOn(subsegment, 'close'); + vi.spyOn(provider, 'setSegment'); // Act provider.instrumentFetch(); diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 33b400a23a..c1ebd7c306 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -1,8 +1,3 @@ -/** - * Test Tracer class - * - * @group unit/tracer/all - */ import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import context from '@aws-lambda-powertools/testing-utils/context'; import type { Context } from 'aws-lambda'; @@ -11,36 +6,24 @@ import { Subsegment, setContextMissingStrategy, } from 'aws-xray-sdk-core'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; 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, - [ - name: string, - fcn: (subsegment?: Subsegment) => unknown, - parent?: Segment | Subsegment, - ] ->; const createCaptureAsyncFuncMock = ( provider: ProviderServiceInterface, subsegment?: Subsegment -): CaptureAsyncFuncMock => - jest +) => + vi .spyOn(provider, 'captureAsyncFunc') .mockImplementation(async (methodName, callBackFn) => { const newSubsegment = subsegment || new Subsegment(`### ${methodName}`); - jest.spyOn(newSubsegment, 'flush').mockImplementation(() => null); + vi.spyOn(newSubsegment, 'flush').mockImplementation(() => null); return await callBackFn(newSubsegment); }); -jest.spyOn(console, 'log').mockImplementation(() => null); -jest.spyOn(console, 'debug').mockImplementation(() => null); -jest.spyOn(console, 'warn').mockImplementation(() => null); -jest.spyOn(console, 'error').mockImplementation(() => null); - describe('Class: Tracer', () => { const ENVIRONMENT_VARIABLES = process.env; const event = { @@ -49,8 +32,8 @@ describe('Class: Tracer', () => { }; beforeEach(() => { - jest.clearAllMocks(); - jest.resetModules(); + vi.clearAllMocks(); + vi.resetModules(); process.env = { ...ENVIRONMENT_VARIABLES }; }); @@ -202,7 +185,7 @@ describe('Class: Tracer', () => { }); describe('Environment Variables configs', () => { - test('when AWS_EXECUTION_ENV environment variable is equal to AWS_Lambda_amplify-mock, tracing is disabled', () => { + it('disables tracing when AWS_EXECUTION_ENV environment variable is equal to AWS_Lambda_amplify-mock', () => { // Prepare process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_amplify-mock'; @@ -217,7 +200,7 @@ describe('Class: Tracer', () => { ); }); - test('when AWS_SAM_LOCAL environment variable is set, tracing is disabled', () => { + it('disables tracing when AWS_SAM_LOCAL environment variable is set', () => { // Prepare process.env.AWS_SAM_LOCAL = 'true'; @@ -232,7 +215,7 @@ describe('Class: Tracer', () => { ); }); - test('when AWS_EXECUTION_ENV environment variable is set, tracing is enabled', () => { + it('leaves tracing enabled when AWS_EXECUTION_ENV environment variable is set', () => { // Prepare process.env.AWS_EXECUTION_ENV = 'nodejs20.x'; @@ -247,7 +230,7 @@ describe('Class: Tracer', () => { ); }); - test('when AWS_EXECUTION_ENV environment variable is NOT set, tracing is disabled', () => { + it('disables tracing when AWS_EXECUTION_ENV environment variable is NOT set', () => { // Prepare process.env.AWS_EXECUTION_ENV = undefined; @@ -262,7 +245,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACE_ENABLED environment variable is set, a tracer with tracing disabled is returned', () => { + it('disables tracing when POWERTOOLS_TRACE_ENABLED environment variable is set to false', () => { // Prepare process.env.POWERTOOLS_TRACE_ENABLED = 'false'; @@ -277,7 +260,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_SERVICE_NAME environment variable is set, a tracer with the correct serviceName is returned', () => { + it('picks up the service name from the POWERTOOLS_SERVICE_NAME environment variable', () => { // Prepare process.env.POWERTOOLS_SERVICE_NAME = 'my-backend-service'; @@ -292,7 +275,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set, a tracer with captureResponse disabled is returned', () => { + it('configures the capture response feature from the POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; @@ -307,7 +290,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set to invalid value, a tracer with captureResponse enabled is returned', () => { + it('ignores invalid values for the POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = ''; @@ -322,7 +305,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set, a tracer with captureError disabled is returned', () => { + it('configures the capture error feature using the POWERTOOLS_TRACER_CAPTURE_ERROR environment variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; @@ -337,7 +320,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set to invalid value, a tracer with captureError enabled is returned', () => { + it('ignores invalid POWERTOOLS_TRACER_CAPTURE_ERROR environment variable values', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = ''; @@ -352,7 +335,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set, captureHTTPsGlobal is called', () => { + it('configures the http instrumentation feature using the POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = 'false'; @@ -367,7 +350,7 @@ describe('Class: Tracer', () => { ); }); - test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set to invalid value, captureHTTPsGlobal is called', () => { + it('ignores invalid values for the POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = ''; @@ -384,10 +367,10 @@ describe('Class: Tracer', () => { }); describe('Method: annotateColdStart', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const putAnnotationSpy = jest.spyOn(tracer, 'putAnnotation'); + const putAnnotationSpy = vi.spyOn(tracer, 'putAnnotation'); // Act tracer.annotateColdStart(); @@ -396,10 +379,10 @@ describe('Class: Tracer', () => { expect(putAnnotationSpy).toBeCalledTimes(0); }); - test('when called multiple times, it annotates true the first time and then false afterwards', () => { + it('annotates the cold start only once', () => { // Prepare const tracer: Tracer = new Tracer(); - const putAnnotationSpy = jest + const putAnnotationSpy = vi .spyOn(tracer, 'putAnnotation') .mockImplementation(() => null); @@ -421,10 +404,10 @@ describe('Class: Tracer', () => { }); describe('Method: addServiceNameAnnotation', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const putAnnotation = jest.spyOn(tracer, 'putAnnotation'); + const putAnnotation = vi.spyOn(tracer, 'putAnnotation'); // Act tracer.addServiceNameAnnotation(); @@ -433,10 +416,10 @@ describe('Class: Tracer', () => { expect(putAnnotation).toBeCalledTimes(0); }); - test('when called while a serviceName has been set, it adds it as annotation', () => { + it('uses the provided service name when one is set', () => { // Prepare const tracer: Tracer = new Tracer({ serviceName: 'foo' }); - const putAnnotation = jest + const putAnnotation = vi .spyOn(tracer, 'putAnnotation') .mockImplementation(() => null); @@ -448,11 +431,11 @@ describe('Class: Tracer', () => { expect(putAnnotation).toBeCalledWith('Service', 'foo'); }); - 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', () => { + it('uses the default service name when one is not provided', () => { // Prepare process.env.POWERTOOLS_SERVICE_NAME = undefined; const tracer: Tracer = new Tracer(); - const putAnnotation = jest + const putAnnotation = vi .spyOn(tracer, 'putAnnotation') .mockImplementation(() => null); @@ -466,10 +449,10 @@ describe('Class: Tracer', () => { }); describe('Method: addResponseAsMetadata', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); // Act tracer.addResponseAsMetadata({ foo: 'bar' }, context.functionName); @@ -478,11 +461,11 @@ describe('Class: Tracer', () => { expect(putMetadataSpy).toBeCalledTimes(0); }); - test('when called while POWERTOOLS_TRACER_CAPTURE_RESPONSE is set to false, it does nothing', () => { + it('does nothing when the feature is disabled via the POWERTOOLS_TRACER_CAPTURE_RESPONSE env variable', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; const tracer: Tracer = new Tracer(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); // Act tracer.addResponseAsMetadata({ foo: 'bar' }, context.functionName); @@ -492,10 +475,10 @@ describe('Class: Tracer', () => { process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = undefined; }); - test('when called with data equal to undefined, it does nothing', () => { + it('it does nothing when the response is undefined', () => { // Prepare const tracer: Tracer = new Tracer(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); // Act tracer.addResponseAsMetadata(undefined, context.functionName); @@ -504,10 +487,10 @@ describe('Class: Tracer', () => { expect(putMetadataSpy).toBeCalledTimes(0); }); - test('when called with default config, it calls tracer.putMetadata correctly', () => { + it('calls the underlying provider method correctly', () => { // Prepare const tracer: Tracer = new Tracer(); - const putMetadataSpy = jest + const putMetadataSpy = vi .spyOn(tracer, 'putMetadata') .mockImplementation(() => null); @@ -524,10 +507,10 @@ describe('Class: Tracer', () => { }); describe('Method: addErrorAsMetadata', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const getSegmentSpy = jest.spyOn(tracer, 'getSegment'); + const getSegmentSpy = vi.spyOn(tracer, 'getSegment'); // Act tracer.addErrorAsMetadata(new Error('foo')); @@ -536,14 +519,14 @@ describe('Class: Tracer', () => { expect(getSegmentSpy).toBeCalledTimes(0); }); - test('when called while POWERTOOLS_TRACER_CAPTURE_ERROR is set to false, it does not capture the error', () => { + it('does not capture the error when called while POWERTOOLS_TRACER_CAPTURE_ERROR is set to false', () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; const tracer: Tracer = new Tracer(); const subsegment = new Subsegment(`## ${context.functionName}`); - jest.spyOn(tracer, 'getSegment').mockImplementation(() => subsegment); - const addErrorFlagSpy = jest.spyOn(subsegment, 'addErrorFlag'); - const addErrorSpy = jest.spyOn(subsegment, 'addError'); + vi.spyOn(tracer, 'getSegment').mockImplementation(() => subsegment); + const addErrorFlagSpy = vi.spyOn(subsegment, 'addErrorFlag'); + const addErrorSpy = vi.spyOn(subsegment, 'addError'); // Act tracer.addErrorAsMetadata(new Error('foo')); @@ -554,13 +537,13 @@ describe('Class: Tracer', () => { process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); - test('when called with default config, it calls subsegment.addError correctly', () => { + it('calls subsegment.addError correctly when called with default config', () => { // Prepare const tracer: Tracer = new Tracer(); const subsegment = new Subsegment(`## ${context.functionName}`); - jest.spyOn(tracer, 'getSegment').mockImplementation(() => subsegment); - const addErrorFlagSpy = jest.spyOn(subsegment, 'addErrorFlag'); - const addErrorSpy = jest.spyOn(subsegment, 'addError'); + vi.spyOn(tracer, 'getSegment').mockImplementation(() => subsegment); + const addErrorFlagSpy = vi.spyOn(subsegment, 'addErrorFlag'); + const addErrorSpy = vi.spyOn(subsegment, 'addError'); // Act tracer.addErrorAsMetadata(new Error('foo')); @@ -571,10 +554,10 @@ describe('Class: Tracer', () => { expect(addErrorSpy).toBeCalledWith(new Error('foo'), false); }); - test('when called and the segment is not found, it returns instead of throwing', () => { + it('returns instead of throwing when called and the segment is not found', () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer, 'getSegment').mockImplementation(() => undefined); + vi.spyOn(tracer, 'getSegment').mockImplementation(() => undefined); // Act & Assess expect(() => tracer.addErrorAsMetadata(new Error('foo'))).not.toThrow(); @@ -582,7 +565,7 @@ describe('Class: Tracer', () => { }); describe('Method: getRootXrayTraceId', () => { - test('when called, it returns the X-Ray trace ID', () => { + it('returns the X-Ray Trace ID when called', () => { // Prepare const tracer: Tracer = new Tracer(); @@ -595,13 +578,13 @@ describe('Class: Tracer', () => { }); describe('Method: getSegment', () => { - test('when called and no segment is returned, it logs a warning', () => { + it('logs a warning when called and no segment is returned', () => { // Prepare const tracer: Tracer = new Tracer(); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => undefined); - jest.spyOn(console, 'warn').mockImplementation(() => null); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => undefined + ); + vi.spyOn(console, 'warn').mockImplementation(() => null); // Act tracer.getSegment(); @@ -612,7 +595,7 @@ describe('Class: Tracer', () => { ); }); - test('when called outside of a namespace or without parent segment, and tracing is disabled, it returns a dummy subsegment', () => { + it('returns a dummy segment when called outside of a namespace or without parent segment, and tracing is disabled', () => { // Prepare process.env.AWS_EXECUTION_ENV = undefined; // This will disable the tracer, simulating local execution const tracer: Tracer = new Tracer(); @@ -625,14 +608,12 @@ describe('Class: Tracer', () => { expect((segment as Subsegment).name).toBe('## Dummy segment'); }); - test('when called within a namespace, it returns the parent segment', () => { + it('returns the parent segment when called within a namespace', () => { // Prepare const tracer: Tracer = new Tracer(); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation( - () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) - ); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) + ); // Act const segment = tracer.getSegment(); @@ -649,7 +630,7 @@ describe('Class: Tracer', () => { }); describe('Method: isTraceSampled', () => { - test('when called, it returns true if the Sampled flag is set', () => { + it('returns true if the Sampled flag is set', () => { // Prepare const tracer: Tracer = new Tracer(); @@ -660,7 +641,7 @@ describe('Class: Tracer', () => { expect(xRayTraceSampled).toBe(false); }); - test('when called and Trace is disabled, it returns false', () => { + it('returns false when called and Trace is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); @@ -673,7 +654,7 @@ describe('Class: Tracer', () => { }); describe('Method: setSegment', () => { - test('when called outside of a namespace or without parent segment, and Tracer is enabled, it throws an error', () => { + it('throws when called outside of a namespace or without parent segment, and Tracer is enabled', () => { // Prepare const tracer: Tracer = new Tracer(); @@ -686,11 +667,11 @@ describe('Class: Tracer', () => { ); }); - test('when called outside of a namespace or without parent segment, and tracing is disabled, it does nothing', () => { + it('does nothing when called outside of a namespace or without parent segment, and tracing is disabled', () => { // Prepare 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'); + const setSegmentSpy = vi.spyOn(tracer.provider, 'setSegment'); // Act const newSubsegment = new Subsegment('## foo.bar'); @@ -700,15 +681,13 @@ describe('Class: Tracer', () => { expect(setSegmentSpy).toBeCalledTimes(0); }); - test('when called within a namespace, it sets the segment', () => { + it('sets the segment as active when called', () => { // Prepare const tracer: Tracer = new Tracer(); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation( - () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) - ); - const providerSetSegmentSpy = jest + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) + ); + const providerSetSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') .mockImplementation(() => ({})); @@ -730,10 +709,10 @@ describe('Class: Tracer', () => { }); describe('Method: putAnnotation', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const putAnnotationSpy = jest.spyOn(tracer.provider, 'putAnnotation'); + const putAnnotationSpy = vi.spyOn(tracer.provider, 'putAnnotation'); // Act tracer.putAnnotation('foo', 'bar'); @@ -742,10 +721,10 @@ describe('Class: Tracer', () => { expect(putAnnotationSpy).toBeCalledTimes(0); }); - test('it calls the provider method with the correct arguments', () => { + it('calls the provider method with the correct arguments', () => { // Prepare const tracer: Tracer = new Tracer(); - const putAnnotationSpy = jest.spyOn(tracer.provider, 'putAnnotation'); + const putAnnotationSpy = vi.spyOn(tracer.provider, 'putAnnotation'); // Act tracer.putAnnotation('foo', 'bar'); @@ -757,10 +736,10 @@ describe('Class: Tracer', () => { }); describe('Method: putMetadata', () => { - test('when tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const putMetadataSpy = jest.spyOn(tracer.provider, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer.provider, 'putMetadata'); // Act tracer.putMetadata('foo', 'bar'); @@ -769,10 +748,10 @@ describe('Class: Tracer', () => { expect(putMetadataSpy).toBeCalledTimes(0); }); - test('it calls the provider method with the correct arguments', () => { + it('calls the provider method with the correct arguments', () => { // Prepare const tracer: Tracer = new Tracer(); - const putMetadataSpy = jest.spyOn(tracer.provider, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer.provider, 'putMetadata'); // Act tracer.putMetadata('foo', 'bar'); @@ -783,10 +762,10 @@ describe('Class: Tracer', () => { expect(putMetadataSpy).toBeCalledWith('foo', 'bar', 'hello-world'); }); - test('when passed a custom namespace, it calls the provider method with the correct arguments', () => { + it('calls the provider method with the correct arguments when a custom namespace is passed directly', () => { // Prepare const tracer: Tracer = new Tracer(); - const putMetadataSpy = jest.spyOn(tracer.provider, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer.provider, 'putMetadata'); // Act tracer.putMetadata('foo', 'bar', 'baz'); @@ -796,10 +775,10 @@ describe('Class: Tracer', () => { expect(putMetadataSpy).toBeCalledWith('foo', 'bar', 'baz'); }); - test('when a custom namespace was set in the constructor, it calls the provider method with the correct arguments', () => { + it('calls the provider method with the correct arguments when the namespace is inferred by the service name', () => { // Prepare const tracer: Tracer = new Tracer({ serviceName: 'baz' }); - const putMetadataSpy = jest.spyOn(tracer.provider, 'putMetadata'); + const putMetadataSpy = vi.spyOn(tracer.provider, 'putMetadata'); // Act tracer.putMetadata('foo', 'bar'); @@ -833,18 +812,13 @@ describe('Class: Tracer', () => { return new Lambda(); }; - test('when used as decorator while tracing is disabled, it does nothing', async () => { + it('does nothing when tracing is disabled', async () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation( - () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) - ); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) ); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); const lambda = getLambdaClass(tracer); // Act @@ -854,34 +828,11 @@ describe('Class: Tracer', () => { expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(0); }); - test('when used as decorator while POWERTOOLS_TRACER_CAPTURE_RESPONSE is set to false, it does not capture the response as metadata', async () => { + it('does not capture the response as metadata when the feature is disabled', async () => { // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); - const lambda = getLambdaClass(tracer); - - // Act - await lambda.handler(event, context); - - // Assess - expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); - expect(putMetadataSpy).toHaveBeenCalledTimes(0); - 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 () => { - // Prepare - const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addResponseAsMetadataSpy = vi.spyOn( tracer, 'addResponseAsMetadata' ); @@ -897,14 +848,11 @@ describe('Class: Tracer', () => { expect(addResponseAsMetadataSpy).toHaveBeenCalledTimes(0); }); - test('when used as decorator while captureResponse is set to true, it captures the response as metadata', async () => { + it('captures the response as metadata by default', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addResponseAsMetadataSpy = vi.spyOn( tracer, 'addResponseAsMetadata' ); @@ -926,47 +874,15 @@ describe('Class: Tracer', () => { ); }); - test('when used as decorator and with standard config, it captures the response as metadata', async () => { - // Prepare - const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( - tracer, - 'addResponseAsMetadata' - ); - const lambda = getLambdaClass(tracer); - - // Act - await lambda.handler(event, context); - - // Assess - expect(captureAsyncFuncSpy).toHaveBeenCalledTimes(1); - expect(captureAsyncFuncSpy).toHaveBeenCalledWith( - '## index.handler', - expect.anything() - ); - expect(addResponseAsMetadataSpy).toHaveBeenCalledTimes(1); - expect(addResponseAsMetadataSpy).toHaveBeenCalledWith( - { foo: 'bar' }, - 'index.handler' - ); - }); - - test('when used as decorator while POWERTOOLS_TRACER_CAPTURE_ERROR is set to false, it does not capture the exceptions', async () => { + it('does not capture exceptions when the feature is disabled', async () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); const newSubsegment = new Subsegment('### dummyMethod'); - jest.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); - const addErrorFlagSpy = jest.spyOn(newSubsegment, 'addErrorFlag'); - const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); + vi.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); + const addErrorFlagSpy = vi.spyOn(newSubsegment, 'addErrorFlag'); + const addErrorSpy = vi.spyOn(newSubsegment, 'addError'); const lambda = getLambdaClass(tracer, { shouldThrow: true }); // Act & Assess @@ -979,18 +895,15 @@ describe('Class: Tracer', () => { process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); - test('when used as decorator and with standard config, it captures the exception', async () => { + it('captures exceptions as metadata by default', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addErrorAsMetadataSpy = jest.spyOn(tracer, 'addErrorAsMetadata'); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addErrorAsMetadataSpy = vi.spyOn(tracer, 'addErrorAsMetadata'); const newSubsegment = new Subsegment('### dummyMethod'); - jest.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); - const addErrorFlagSpy = jest.spyOn(newSubsegment, 'addErrorFlag'); - const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); + vi.spyOn(tracer, 'getSegment').mockImplementation(() => newSubsegment); + const addErrorFlagSpy = vi.spyOn(newSubsegment, 'addErrorFlag'); + const addErrorSpy = vi.spyOn(newSubsegment, 'addError'); const lambda = getLambdaClass(tracer, { shouldThrow: true }); // Act & Assess @@ -1003,11 +916,11 @@ describe('Class: Tracer', () => { expect.assertions(6); }); - test('when used as decorator and with standard config, it annotates ColdStart', async () => { + it('adds the ColdStart annotation', async () => { // Prepare const tracer: Tracer = new Tracer(); const captureAsyncFuncSpy = createCaptureAsyncFuncMock(tracer.provider); - const annotateColdStartSpy = jest.spyOn(tracer, 'annotateColdStart'); + const annotateColdStartSpy = vi.spyOn(tracer, 'annotateColdStart'); const lambda = getLambdaClass(tracer); // Act @@ -1022,11 +935,11 @@ describe('Class: Tracer', () => { expect(annotateColdStartSpy).toHaveBeenCalledTimes(1); }); - test('when used as decorator and with standard config, it adds the Service annotation', async () => { + it('adds the Service annotation', async () => { // Prepare const tracer: Tracer = new Tracer(); const captureAsyncFuncSpy = createCaptureAsyncFuncMock(tracer.provider); - const addServiceNameAnnotationSpy = jest.spyOn( + const addServiceNameAnnotationSpy = vi.spyOn( tracer, 'addServiceNameAnnotation' ); @@ -1045,20 +958,20 @@ describe('Class: Tracer', () => { expect(addServiceNameAnnotationSpy).toHaveBeenCalledTimes(1); }); - test('when used as decorator on an async method, the method is awaited correctly', async () => { + it('awaits async methods correctly', async () => { // Prepare const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '### dummyMethod' ); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment + ); setContextMissingStrategy(() => null); - const subsegmentCloseSpy = jest + const subsegmentCloseSpy = vi .spyOn(newSubsegment, 'close') - .mockImplementation(); + .mockImplementation(() => null); createCaptureAsyncFuncMock(tracer.provider, newSubsegment); class Lambda implements LambdaInterface { @@ -1088,7 +1001,7 @@ describe('Class: Tracer', () => { } } const lambda = new Lambda('someValue'); - const otherDummyMethodSpy = jest.spyOn(lambda, 'otherDummyMethod'); + const otherDummyMethodSpy = vi.spyOn(lambda, 'otherDummyMethod'); // Act const handler = lambda.handler.bind(lambda); @@ -1110,17 +1023,17 @@ describe('Class: Tracer', () => { const tracer: Tracer = new Tracer(); const handlerSubsegment: Segment | Subsegment | undefined = new Subsegment('### dummyMethod'); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => handlerSubsegment); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => handlerSubsegment + ); setContextMissingStrategy(() => null); - jest - .spyOn(tracer.provider, 'captureAsyncFunc') - .mockImplementation(async (methodName, callBackFn) => { + vi.spyOn(tracer.provider, 'captureAsyncFunc').mockImplementation( + async (methodName, callBackFn) => { await callBackFn(handlerSubsegment); - }); - const logWarningSpy = jest.spyOn(console, 'warn'); - const closeSpy = jest + } + ); + const logWarningSpy = vi.spyOn(console, 'warn'); + const closeSpy = vi .spyOn(handlerSubsegment, 'close') .mockImplementation(() => { throw new Error('dummy error'); @@ -1142,13 +1055,10 @@ describe('Class: Tracer', () => { }); describe('Method: captureMethod', () => { - test('when called while tracing is disabled, it does nothing', async () => { + it('does nothing when tracing is disabled', async () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); class Lambda implements LambdaInterface { @tracer.captureMethod() public async dummyMethod(some: string): Promise { @@ -1170,14 +1080,11 @@ describe('Class: Tracer', () => { expect(captureAsyncFuncSpy).toBeCalledTimes(0); }); - test('when used as decorator and with standard config, it captures the response as metadata', async () => { + it('captures the response as metdata by default', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addResponseAsMetadataSpy = vi.spyOn( tracer, 'addResponseAsMetadata' ); @@ -1212,14 +1119,11 @@ describe('Class: Tracer', () => { ); }); - test('when used as decorator and with captureResponse set to false, it does not capture the response as metadata', async () => { + it('does not capture the response as metadata when the feature is disabled', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addResponseAsMetadataSpy = vi.spyOn( tracer, 'addResponseAsMetadata' ); @@ -1250,14 +1154,11 @@ describe('Class: Tracer', () => { expect(addResponseAsMetadataSpy).toHaveBeenCalledTimes(0); }); - test('when used as decorator and with captureResponse set to true, it does captures the response as metadata', async () => { + it('captures the response as methadata when the feature is enabled', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); - const addResponseAsMetadataSpy = jest.spyOn( + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); + const addResponseAsMetadataSpy = vi.spyOn( tracer, 'addResponseAsMetadata' ); @@ -1288,18 +1189,18 @@ describe('Class: Tracer', () => { expect(addResponseAsMetadataSpy).toHaveBeenCalledTimes(1); }); - test('when used as decorator and with standard config, it captures the exception correctly', async () => { + it('captures the exception correctly', async () => { // Prepare const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '### dummyMethod' ); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment + ); setContextMissingStrategy(() => null); const captureAsyncFuncSpy = createCaptureAsyncFuncMock(tracer.provider); - const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); + const addErrorSpy = vi.spyOn(newSubsegment, 'addError'); class Lambda implements LambdaInterface { @tracer.captureMethod() public async dummyMethod(_some: string): Promise { @@ -1333,51 +1234,15 @@ describe('Class: Tracer', () => { expect.assertions(6); }); - test('when used as decorator and when calling other methods/props in the class they are called in the orginal scope', async () => { + it('preserves the this scope correctly when used as decorator', 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 { - @tracer.captureMethod() - public async dummyMethod(): Promise { - return `otherMethod:${this.otherMethod()}`; - } - - @tracer.captureLambdaHandler() - public async handler( - _event: unknown, - _context: Context - ): Promise { - return await this.dummyMethod(); - } - - public otherMethod(): string { - return 'otherMethod'; - } - } - - // Act / Assess - expect(await new Lambda().handler({}, context)).toEqual( - 'otherMethod:otherMethod' - ); - }); - - test('when used as decorator and when calling a method in the class, it has access to member variables', async () => { - // Prepare - const tracer: Tracer = new Tracer(); - const newSubsegment: Segment | Subsegment | undefined = new Subsegment( - '### dummyMethod' + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment ); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); setContextMissingStrategy(() => null); class Lambda implements LambdaInterface { @@ -1406,20 +1271,20 @@ describe('Class: Tracer', () => { expect(await lambda.dummyMethod()).toEqual('memberVariable:someValue'); }); - test('when used as decorator on an async method, the method is awaited correctly', async () => { + it('awaits the async method correctly when used as decorator', async () => { // Prepare const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '### dummyMethod' ); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment + ); setContextMissingStrategy(() => null); - const subsegmentCloseSpy = jest + const subsegmentCloseSpy = vi .spyOn(newSubsegment, 'close') - .mockImplementation(); + .mockImplementation(() => null); createCaptureAsyncFuncMock(tracer.provider, newSubsegment); class Lambda implements LambdaInterface { @@ -1445,9 +1310,7 @@ describe('Class: Tracer', () => { // Act const lambda = new Lambda(); - const otherDummyMethodSpy = jest - .spyOn(lambda, 'otherDummyMethod') - .mockImplementation(); + const otherDummyMethodSpy = vi.spyOn(lambda, 'otherDummyMethod'); const handler = lambda.handler.bind(lambda); await handler({}, context); @@ -1460,13 +1323,10 @@ describe('Class: Tracer', () => { expect(dummyCallOrder).toBeLessThan(otherDummyCallOrder); }); - test('when used as decorator together with another external decorator, the method name is detected properly', async () => { + it('detects the method name correctly when used as decorator together with another external decorator', async () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' - ); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); function passThrough() { // A decorator that calls the original method. @@ -1512,21 +1372,18 @@ describe('Class: Tracer', () => { ); }); - test('when used as decorator and with a custom subSegmentName, it sets the correct name for the subsegment', async () => { + it('sets the correct name for the subsegment when used as decorator and with a custom subSegmentName', async () => { // Prepare const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '### dummyMethod' ); - jest.spyOn(newSubsegment, 'flush').mockImplementation(() => null); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); - setContextMissingStrategy(() => null); - const captureAsyncFuncSpy = jest.spyOn( - tracer.provider, - 'captureAsyncFunc' + vi.spyOn(newSubsegment, 'flush').mockImplementation(() => null); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment ); + setContextMissingStrategy(() => null); + const captureAsyncFuncSpy = vi.spyOn(tracer.provider, 'captureAsyncFunc'); class Lambda implements LambdaInterface { @tracer.captureMethod({ subSegmentName: '#### myCustomMethod' }) public async dummyMethod(some: string): Promise { @@ -1557,17 +1414,17 @@ describe('Class: Tracer', () => { const tracer: Tracer = new Tracer(); const handlerSubsegment: Segment | Subsegment | undefined = new Subsegment('### dummyMethod'); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => handlerSubsegment); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => handlerSubsegment + ); setContextMissingStrategy(() => null); - jest - .spyOn(tracer.provider, 'captureAsyncFunc') - .mockImplementation(async (methodName, callBackFn) => { + vi.spyOn(tracer.provider, 'captureAsyncFunc').mockImplementation( + async (methodName, callBackFn) => { await callBackFn(handlerSubsegment); - }); - const logWarningSpy = jest.spyOn(console, 'warn'); - const closeSpy = jest + } + ); + const logWarningSpy = vi.spyOn(console, 'warn'); + const closeSpy = vi .spyOn(handlerSubsegment, 'close') .mockImplementation(() => { throw new Error('dummy error'); @@ -1602,10 +1459,10 @@ describe('Class: Tracer', () => { }); describe('Method: captureAWS', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when called while tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const captureAWSSpy = jest + const captureAWSSpy = vi .spyOn(tracer.provider, 'captureAWS') .mockImplementation(() => null); @@ -1616,10 +1473,10 @@ describe('Class: Tracer', () => { expect(captureAWSSpy).toBeCalledTimes(0); }); - test('when called it returns the decorated object that was passed to it', () => { + it('returns the decorated object that was passed to it', () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAWSSpy = jest + const captureAWSSpy = vi .spyOn(tracer.provider, 'captureAWS') .mockImplementation(() => null); @@ -1633,10 +1490,10 @@ describe('Class: Tracer', () => { }); describe('Method: captureAWSv3Client', () => { - test('when called while tracing is disabled, it does nothing', () => { + it('does nothing when tracing is disabled', () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const captureAWSv3ClientSpy = jest + const captureAWSv3ClientSpy = vi .spyOn(tracer.provider, 'captureAWSv3Client') .mockImplementation(() => null); @@ -1647,10 +1504,10 @@ describe('Class: Tracer', () => { expect(captureAWSv3ClientSpy).toBeCalledTimes(0); }); - test('when called it returns the decorated object that was passed to it', () => { + it('returns the decorated object that was passed to it', () => { // Prepare const tracer: Tracer = new Tracer(); - const captureAWSv3ClientSpy = jest + const captureAWSv3ClientSpy = vi .spyOn(tracer.provider, 'captureAWSv3Client') .mockImplementation(() => null); @@ -1662,74 +1519,4 @@ describe('Class: Tracer', () => { expect(captureAWSv3ClientSpy).toBeCalledWith({}); }); }); - - describe('Method: captureAWSClient', () => { - test('when called while tracing is disabled, it does nothing', () => { - // Prepare - const tracer: Tracer = new Tracer({ enabled: false }); - const captureAWSClientSpy = jest.spyOn( - tracer.provider, - 'captureAWSClient' - ); - - // Act - tracer.captureAWSClient({}); - - // Assess - expect(captureAWSClientSpy).toBeCalledTimes(0); - }); - - test('when called with a base AWS SDK v2 client, it calls the provider method to patch it', () => { - // Prepare - const tracer: Tracer = new Tracer(); - const captureAWSClientSpy = jest - .spyOn(tracer.provider, 'captureAWSClient') - .mockImplementation(() => null); - - // Act - tracer.captureAWSClient({}); - - // Assess - expect(captureAWSClientSpy).toBeCalledTimes(1); - expect(captureAWSClientSpy).toBeCalledWith({}); - }); - - test('when called with a complex AWS SDK v2 client, it calls the provider method to patch it', () => { - // Prepare - const tracer: Tracer = new Tracer(); - const captureAWSClientSpy = jest - .spyOn(tracer.provider, 'captureAWSClient') - .mockImplementationOnce(() => { - throw new Error('service.customizeRequests is not a function'); - }) - .mockImplementation(() => null); - - // Act - // This is the shape of a DocumentClient from the AWS SDK v2 - tracer.captureAWSClient({ service: {} }); - - // Assess - expect(captureAWSClientSpy).toBeCalledTimes(2); - expect(captureAWSClientSpy).toHaveBeenNthCalledWith(1, { service: {} }); - expect(captureAWSClientSpy).toHaveBeenNthCalledWith(2, {}); - }); - - test('when called with an uncompatible object, it throws an error', () => { - // Prepare - const tracer: Tracer = new Tracer(); - const captureAWSClientSpy = jest.spyOn( - tracer.provider, - 'captureAWSClient' - ); - - // Act / Assess - expect(() => { - tracer.captureAWSClient({}); - }).toThrow('service.customizeRequests is not a function'); - expect(captureAWSClientSpy).toBeCalledTimes(2); - expect(captureAWSClientSpy).toHaveBeenNthCalledWith(1, {}); - expect(captureAWSClientSpy).toHaveBeenNthCalledWith(2, undefined); - expect.assertions(4); - }); - }); }); diff --git a/packages/tracer/tests/unit/middy.test.ts b/packages/tracer/tests/unit/middy.test.ts index 561a403cc3..eebf3cde48 100644 --- a/packages/tracer/tests/unit/middy.test.ts +++ b/packages/tracer/tests/unit/middy.test.ts @@ -1,8 +1,3 @@ -/** - * Test Tracer middleware - * - * @group unit/tracer/all - */ import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; import context from '@aws-lambda-powertools/testing-utils/context'; import middy from '@middy/core'; @@ -12,19 +7,16 @@ import { Subsegment, setContextMissingStrategy, } from 'aws-xray-sdk-core'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; 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); -jest.spyOn(console, 'error').mockImplementation(() => null); - describe('Middy middleware', () => { const ENVIRONMENT_VARIABLES = process.env; beforeEach(() => { - jest.clearAllMocks(); - jest.resetModules(); + vi.clearAllMocks(); + vi.resetModules(); process.env = { ...ENVIRONMENT_VARIABLES }; }); @@ -32,13 +24,13 @@ describe('Middy middleware', () => { process.env = ENVIRONMENT_VARIABLES; }); describe('Middleware: captureLambdaHandler', () => { - test('when used while tracing is disabled, it does nothing', async () => { + it('does nothing when used while tracing is disabled', async () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const setSegmentSpy = jest + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') - .mockImplementation(); - const getSegmentSpy = jest + .mockImplementation(() => null); + const getSegmentSpy = vi .spyOn(tracer.provider, 'getSegment') .mockImplementationOnce( () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) @@ -60,13 +52,13 @@ describe('Middy middleware', () => { expect(getSegmentSpy).toHaveBeenCalledTimes(0); }); - test('when used while tracing is disabled, even if the handler throws an error, it does nothing', async () => { + it('does nothing when used while tracing is disabled, even if the handler throws an error', async () => { // Prepare const tracer: Tracer = new Tracer({ enabled: false }); - const setSegmentSpy = jest + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') - .mockImplementation(); - const getSegmentSpy = jest + .mockImplementation(() => null); + const getSegmentSpy = vi .spyOn(tracer.provider, 'getSegment') .mockImplementationOnce( () => new Segment('facade', process.env._X_AMZN_TRACE_ID || null) @@ -89,12 +81,12 @@ describe('Middy middleware', () => { expect.assertions(3); }); - test('when used while POWERTOOLS_TRACER_CAPTURE_RESPONSE is set to false, it does not capture the response as metadata', async () => { + it('does not capture the response when used while POWERTOOLS_TRACER_CAPTURE_RESPONSE is set to false', async () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => null); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -108,11 +100,11 @@ describe('Middy middleware', () => { process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = undefined; }); - test('when used while captureResponse set to false, it does not capture the response as metadata', async () => { + it('does not capture the resposne as metadata when used while captureResponse set to false', async () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => null); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -125,11 +117,11 @@ describe('Middy middleware', () => { expect(putMetadataSpy).toHaveBeenCalledTimes(0); }); - test('when used while captureResponse set to true, it captures the response as metadata', async () => { + it('captures the response as metadata when used while captureResponse set to true', async () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => null); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -145,11 +137,11 @@ describe('Middy middleware', () => { }); }); - test('when used with standard config, it captures the response as metadata', async () => { + it('captures the response as metadata when used with standard config', async () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(); - const putMetadataSpy = jest.spyOn(tracer, 'putMetadata'); + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => null); + const putMetadataSpy = vi.spyOn(tracer, 'putMetadata'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -165,22 +157,22 @@ describe('Middy middleware', () => { }); }); - test('when used while POWERTOOLS_TRACER_CAPTURE_ERROR is set to false, it does not capture the exceptions', async () => { + it('does not capture exceptions when used while POWERTOOLS_TRACER_CAPTURE_ERROR is set to false', async () => { // Prepare process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '## index.handler' ); - const setSegmentSpy = jest + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') - .mockImplementation(); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); + .mockImplementation(() => null); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment + ); setContextMissingStrategy(() => null); - const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); - const addErrorFlagSpy = jest.spyOn(newSubsegment, 'addErrorFlag'); + const addErrorSpy = vi.spyOn(newSubsegment, 'addError'); + const addErrorFlagSpy = vi.spyOn(newSubsegment, 'addErrorFlag'); const lambdaHandler: Handler = async ( _event: unknown, _context: Context @@ -202,20 +194,20 @@ describe('Middy middleware', () => { process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = undefined; }); - test('when used with standard config, it captures the exception correctly', async () => { + it('captures the exception correctly when used with standard config', async () => { // Prepare const tracer: Tracer = new Tracer(); const newSubsegment: Segment | Subsegment | undefined = new Subsegment( '## index.handler' ); - const setSegmentSpy = jest + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') - .mockImplementation(); - jest - .spyOn(tracer.provider, 'getSegment') - .mockImplementation(() => newSubsegment); + .mockImplementation(() => null); + vi.spyOn(tracer.provider, 'getSegment').mockImplementation( + () => newSubsegment + ); setContextMissingStrategy(() => null); - const addErrorSpy = jest.spyOn(newSubsegment, 'addError'); + const addErrorSpy = vi.spyOn(newSubsegment, 'addError'); const lambdaHandler: Handler = async ( _event: unknown, _context: Context @@ -238,17 +230,16 @@ describe('Middy middleware', () => { expect.assertions(5); }); - test('when used with standard config, it annotates ColdStart correctly', async () => { + it('annotates the ColdStart correctly when used with standard config', async () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(() => ({})); - jest - .spyOn(tracer.provider, 'getSegment') + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => ({})); + vi.spyOn(tracer.provider, 'getSegment') .mockImplementationOnce(() => new Segment('facade')) .mockImplementationOnce(() => new Subsegment('## index.handler')) .mockImplementationOnce(() => new Segment('facade')) .mockImplementation(() => new Subsegment('## index.handler')); - const putAnnotationSpy = jest.spyOn(tracer, 'putAnnotation'); + const putAnnotationSpy = vi.spyOn(tracer, 'putAnnotation'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -265,15 +256,14 @@ describe('Middy middleware', () => { expect(putAnnotationSpy).toHaveBeenNthCalledWith(3, 'ColdStart', false); }); - test('when used with standard config, it annotates Service correctly', async () => { + it('annotates the Service correctly when used with standard config', async () => { // Prepare const tracer: Tracer = new Tracer(); - jest.spyOn(tracer.provider, 'setSegment').mockImplementation(() => ({})); - jest - .spyOn(tracer.provider, 'getSegment') + vi.spyOn(tracer.provider, 'setSegment').mockImplementation(() => ({})); + vi.spyOn(tracer.provider, 'getSegment') .mockImplementationOnce(() => new Segment('facade')) .mockImplementation(() => new Subsegment('## index.handler')); - const putAnnotationSpy = jest.spyOn(tracer, 'putAnnotation'); + const putAnnotationSpy = vi.spyOn(tracer, 'putAnnotation'); const handler = middy(async (_event: unknown, _context: Context) => ({ foo: 'bar', @@ -292,28 +282,27 @@ describe('Middy middleware', () => { ); }); - test('when enabled, and another middleware returns early, it still closes and restores the segments correctly', async () => { + it('closes and restores segments correctly when another middleware returns early', async () => { // Prepare const tracer = new Tracer(); - const setSegmentSpy = jest + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') .mockImplementation(() => ({})); - jest.spyOn(tracer, 'annotateColdStart').mockImplementation(() => ({})); - jest - .spyOn(tracer, 'addServiceNameAnnotation') - .mockImplementation(() => ({})); + vi.spyOn(tracer, 'annotateColdStart').mockImplementation(() => ({})); + vi.spyOn(tracer, 'addServiceNameAnnotation').mockImplementation( + () => ({}) + ); const facadeSegment1 = new Segment('facade'); const handlerSubsegment1 = new Subsegment('## index.handlerA'); - jest - .spyOn(facadeSegment1, 'addNewSubsegment') - .mockImplementation(() => handlerSubsegment1); + vi.spyOn(facadeSegment1, 'addNewSubsegment').mockImplementation( + () => handlerSubsegment1 + ); const facadeSegment2 = new Segment('facade'); const handlerSubsegment2 = new Subsegment('## index.handlerB'); - jest - .spyOn(facadeSegment2, 'addNewSubsegment') - .mockImplementation(() => handlerSubsegment2); - jest - .spyOn(tracer.provider, 'getSegment') + vi.spyOn(facadeSegment2, 'addNewSubsegment').mockImplementation( + () => handlerSubsegment2 + ); + vi.spyOn(tracer.provider, 'getSegment') .mockImplementationOnce(() => facadeSegment1) .mockImplementationOnce(() => facadeSegment2); const myCustomMiddleware = (): middy.MiddlewareObj => { @@ -360,25 +349,22 @@ describe('Middy middleware', () => { const tracer = new Tracer(); const facadeSegment = new Segment('facade'); const handlerSubsegment = new Subsegment('## index.handler'); - jest - .spyOn(tracer.provider, 'getSegment') + vi.spyOn(tracer.provider, 'getSegment') .mockImplementationOnce(() => facadeSegment) .mockImplementationOnce(() => handlerSubsegment); - jest.spyOn(tracer, 'annotateColdStart').mockImplementation(() => ({})); - jest - .spyOn(tracer, 'addServiceNameAnnotation') - .mockImplementation(() => ({})); - const setSegmentSpy = jest + vi.spyOn(tracer, 'annotateColdStart').mockImplementation(() => ({})); + vi.spyOn(tracer, 'addServiceNameAnnotation').mockImplementation(() => ({})); + const setSegmentSpy = vi .spyOn(tracer.provider, 'setSegment') .mockImplementation(() => ({})); - jest - .spyOn(facadeSegment, 'addNewSubsegment') - .mockImplementation(() => handlerSubsegment); + vi.spyOn(facadeSegment, 'addNewSubsegment').mockImplementation( + () => handlerSubsegment + ); const handler = middy((): void => { console.log('Hello world!'); }).use(captureLambdaHandler(tracer)); - const logWarningSpy = jest.spyOn(console, 'warn'); - const closeSpy = jest + const logWarningSpy = vi.spyOn(console, 'warn'); + const closeSpy = vi .spyOn(handlerSubsegment, 'close') .mockImplementation(() => { throw new Error('dummy error'); diff --git a/packages/tracer/vitest.config.ts b/packages/tracer/vitest.config.ts new file mode 100644 index 0000000000..9f1196ef1f --- /dev/null +++ b/packages/tracer/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + environment: 'node', + setupFiles: ['../testing/src/setupEnv.ts'], + }, +}); diff --git a/vitest.config.ts b/vitest.config.ts index 86ff981bfb..5bbd1d007c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ 'packages/parameters/**', 'packages/parser/**', 'packages/testing/**', - 'packages/tracer/**', + 'packages/tracer/src/types/**', ], }, setupFiles: ['./packages/testing/src/setupEnv.ts'],