From 6bd7fb18258427afa1ae86d92fbaa0778ae7dc09 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 12:11:54 +0600 Subject: [PATCH 01/95] test: cleanup unit tests --- packages/metrics/tests/unit/Metrics.test.ts | 787 -------------------- 1 file changed, 787 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index a3960b9635..17ccb06046 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -4,25 +4,10 @@ * @group unit/metrics/class */ -import { ContextExamples as dummyContext, Events as dummyEvent, LambdaInterface } from '@aws-lambda-powertools/commons'; -import { Context, Callback } from 'aws-lambda'; - -import { Metrics, MetricUnits, MetricResolution } from '../../src/'; - -const MAX_METRICS_SIZE = 100; -const MAX_DIMENSION_COUNT = 29; -const DEFAULT_NAMESPACE = 'default_namespace'; - const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); -interface LooseObject { - [key: string]: string -} - describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; - const event = dummyEvent.Custom.CustomEvent; beforeEach(() => { consoleSpy.mockClear(); @@ -31,776 +16,4 @@ describe('Class: Metrics', () => { beforeAll(() => { process.env = { ...ENVIRONMENT_VARIABLES }; }); - - describe('Feature: Dimensions logging', () => { - test('should log service dimension correctly when passed', () => { - const serviceName = 'testing_name'; - - const metrics = new Metrics({ namespace: 'test', serviceName: serviceName }); - metrics.addMetric('test_name', MetricUnits.Seconds, 14); - const loggedData = metrics.serializeMetrics(); - - expect(loggedData.service).toEqual(serviceName); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(1); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0][0]).toEqual('service'); - }); - - test('should log service dimension correctly from env var when not passed', () => { - const serviceName = 'hello-world-service'; - process.env.POWERTOOLS_SERVICE_NAME = serviceName; - - const metrics = new Metrics({ namespace: 'test' }); - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - const loggedData = metrics.serializeMetrics(); - - expect(loggedData.service).toEqual(serviceName); - }); - - test('Additional dimensions should be added correctly', () => { - const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - const metrics = new Metrics({ namespace: 'test' }); - - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addDimension(additionalDimension.name, additionalDimension.value); - const loggedData = metrics.serializeMetrics(); - - // Expect the additional dimension, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); - expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); - }); - - test('Publish Stored Metrics should clear added dimensions', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const dimensionItem = { name: 'dimensionName', value: 'dimensionValue' }; - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name_1', MetricUnits.Count, 1); - metrics.addDimension(dimensionItem.name, dimensionItem.value); - metrics.publishStoredMetrics(); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = [ JSON.parse(consoleSpy.mock.calls[0][0]), JSON.parse(consoleSpy.mock.calls[1][0]) ]; - - expect(console.log).toBeCalledTimes(2); - expect(loggedData[0][dimensionItem.name]).toEqual(dimensionItem.value); - // Expect the additional dimension, and the service dimension - expect(loggedData[0]._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); - expect(loggedData[1][dimensionItem.name]).toBeUndefined(); - // Expect just the service dimension - expect(loggedData[1]._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(1); - }); - - test('Adding more than max dimensions should throw error', () => { - expect.assertions(1); - const metrics = new Metrics(); - // The service dimension is already set, so start from 1 - for (let x = 1; x < MAX_DIMENSION_COUNT; x++) { - metrics.addDimension(`Dimension-${x}`, `value-${x}`); - } - try { - metrics.addDimension(`Dimension-${MAX_DIMENSION_COUNT}`, `value-${MAX_DIMENSION_COUNT}`); - } catch (e) { - expect((e).message).toBe(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - } - }); - - test('Additional bulk dimensions should be added correctly', () => { - const additionalDimensions: LooseObject = { dimension2: 'dimension2Value', dimension3: 'dimension3Value' }; - const metrics = new Metrics({ namespace: 'test' }); - - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addDimensions(additionalDimensions); - const loggedData = metrics.serializeMetrics(); - - // Expect the additional dimensions, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); - Object.keys(additionalDimensions).forEach((key) => { - expect(loggedData[key]).toEqual(additionalDimensions[key]); - }); - }); - - test('Bulk Adding more than max dimensions should throw error', () => { - expect.assertions(1); - const metrics = new Metrics(); - const additionalDimensions: LooseObject = {}; - - metrics.addDimension('Dimension-Initial', 'Dimension-InitialValue'); - for (let x = 0; x < MAX_DIMENSION_COUNT; x++) { - additionalDimensions[`dimension${x}`] = `dimension${x}Value`; - } - - try { - metrics.addDimensions(additionalDimensions); - } catch (e) { - expect((e).message).toBe( - `Unable to add ${ - Object.keys(additionalDimensions).length - } dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`, - ); - } - }); - }); - - describe('Feature: Metadata', () => { - test('Metadata should be added correctly', () => { - const metadataItem = { name: 'metaName', value: 'metaValue' }; - - const metrics = new Metrics({ namespace: 'test' }); - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addMetadata(metadataItem.name, metadataItem.value); - - const loggedData = metrics.serializeMetrics(); - metrics.clearMetadata(); - const postClearLoggedData = metrics.serializeMetrics(); - - expect(loggedData[metadataItem.name]).toEqual(metadataItem.value); - expect(postClearLoggedData[metadataItem.name]).toBeUndefined(); - }); - - test('Publish Stored Metrics should clear metadata', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const metadataItem = { name: 'metaName', value: 'metaValue' }; - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name_1', MetricUnits.Count, 1); - metrics.addMetadata(metadataItem.name, metadataItem.value); - metrics.publishStoredMetrics(); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = [ JSON.parse(consoleSpy.mock.calls[0][0]), JSON.parse(consoleSpy.mock.calls[1][0]) ]; - - expect(console.log).toBeCalledTimes(2); - expect(loggedData[0][metadataItem.name]).toEqual(metadataItem.value); - expect(loggedData[1][metadataItem.name]).toBeUndefined(); - }); - }); - - describe('Feature: Default Dimensions', () => { - test('Adding more than max default dimensions should throw error', async () => { - expect.assertions(1); - - const defaultDimensions: LooseObject = {}; - for (let x = 0; x < MAX_DIMENSION_COUNT + 1; x++) { - defaultDimensions[`dimension-${x}`] = `value-${x}`; - } - - const metrics = new Metrics({ namespace: 'test' }); - - try { - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: defaultDimensions }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - return; - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - } catch (e) { - expect((e).message).toBe('Max dimension count hit'); - } - }); - - test('Clearing dimensions should only remove added dimensions, not default', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: { default: 'defaultValue' } }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addDimension(additionalDimension.name, additionalDimension.value); - const loggedData = metrics.serializeMetrics(); - // Expect the additional dimensions, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); - expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); - metrics.clearDimensions(); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - expect(console.log).toBeCalledTimes(1); - // Expect the additional dimension, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain('default'); - expect(loggedData.default).toContain('defaultValue'); - }); - - test('Clearing default dimensions should only remove default dimensions, not added', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: { default: 'defaultValue' } }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addDimension(additionalDimension.name, additionalDimension.value); - metrics.clearDefaultDimensions(); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - expect(console.log).toBeCalledTimes(1); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(1); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain(additionalDimension.name); - expect(loggedData[additionalDimension.name]).toContain(additionalDimension.value); - }); - }); - - describe('Feature: Cold Start', () => { - test('Cold start metric should only be written out once and flushed automatically', async () => { - const metrics = new Metrics({ namespace: 'test' }); - - const handler = async (_event: unknown, _context: Context): Promise => { - // Should generate only one log - metrics.captureColdStartMetric(); - }; - - await handler(event, context); - await handler(event, context); - const loggedData = [JSON.parse(consoleSpy.mock.calls[0][0])]; - - expect(console.log).toBeCalledTimes(1); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics[0].Name).toBe('ColdStart'); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics[0].Unit).toBe('Count'); - expect(loggedData[0].ColdStart).toBe(1); - }); - - test('Cold start metric should only be written out once', async () => { - const metrics = new Metrics({ namespace: 'test' }); - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ captureColdStartMetric: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked again!')); - const loggedData = [ JSON.parse(consoleSpy.mock.calls[0][0]), JSON.parse(consoleSpy.mock.calls[1][0]) ]; - - expect(console.log).toBeCalledTimes(3); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics[0].Name).toBe('ColdStart'); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics[0].Unit).toBe('Count'); - expect(loggedData[0].ColdStart).toBe(1); - }); - - test('Cold should have service and function name if present', async () => { - const serviceName = 'test-service'; - const metrics = new Metrics({ namespace: 'test', serviceName: serviceName }); - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ captureColdStartMetric: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - } - } - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - expect(console.log).toBeCalledTimes(2); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Name).toBe('ColdStart'); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Unit).toBe('Count'); - expect(loggedData.service).toBe(serviceName); - expect(loggedData.function_name).toBe(context.functionName); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain('service'); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain('function_name'); - expect(loggedData.ColdStart).toBe(1); - }); - - test('Cold should still log, without a function name', async () => { - const serviceName = 'test-service'; - const metrics = new Metrics({ namespace: 'test', serviceName: serviceName }); - const newContext = JSON.parse(JSON.stringify(context)); - delete newContext.functionName; - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ captureColdStartMetric: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - } - } - - await new LambdaFunction().handler(event, newContext, () => console.log('Lambda invoked!')); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - expect(console.log).toBeCalledTimes(2); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Name).toBe('ColdStart'); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Unit).toBe('Count'); - expect(loggedData.service).toBe(serviceName); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain('service'); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).not.toContain('function_name'); - expect(loggedData.ColdStart).toBe(1); - }); - }); - - describe('Feature: throwOnEmptyMetrics', () => { - test('Error should be thrown on empty metrics when throwOnEmptyMetrics is passed', async () => { - expect.assertions(1); - - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ throwOnEmptyMetrics: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - return; - } - } - - try { - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - } catch (e) { - expect((e).message).toBe('The number of metrics recorded must be higher than zero'); - } - }); - - test('Error should not be thrown on empty metrics if throwOnEmptyMetrics is set to false', async () => { - expect.assertions(1); - - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ throwOnEmptyMetrics: false }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - return; - } - } - - try { - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - } catch (e) { - fail(`Should not throw but got the following Error: ${e}`); - } - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(0); - }); - - test('Error should be thrown on empty metrics when throwOnEmptyMetrics() is called', async () => { - expect.assertions(1); - - const metrics = new Metrics({ namespace: 'test' }); - const handler = async (_event: unknown, _context: Context): Promise => { - metrics.throwOnEmptyMetrics(); - // Logic goes here - metrics.publishStoredMetrics(); - }; - - try { - await handler(event, context); - } catch (e) { - expect((e).message).toBe('The number of metrics recorded must be higher than zero'); - } - }); - }); - - describe('Feature: Auto log at limit', () => { - test('Logger should write out block when limit is reached', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const extraCount = 10; - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - for (let x = 0; x < MAX_METRICS_SIZE + extraCount; x++) { - metrics.addMetric(`test_name_${x}`, MetricUnits.Count, x); - } - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = [ JSON.parse(consoleSpy.mock.calls[0][0]), JSON.parse(consoleSpy.mock.calls[1][0]) ]; - - expect(console.log).toBeCalledTimes(2); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics.length).toBe(100); - expect(loggedData[1]._aws.CloudWatchMetrics[0].Metrics.length).toBe(extraCount); - }); - }); - - describe('Feature: Output validation ', () => { - - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - beforeEach(() => { - consoleSpy.mockClear(); - }); - - test('Should use default namespace if no namespace is set', () => { - delete process.env.POWERTOOLS_METRICS_NAMESPACE; - const metrics = new Metrics(); - - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Namespace).toBe(DEFAULT_NAMESPACE); - expect(console.warn).toHaveBeenNthCalledWith(1, 'Namespace should be defined, default used'); - }); - - test('Should contain a metric value if added once', ()=> { - const metrics = new Metrics(); - - metrics.addMetric('test_name', MetricUnits.Count, 1); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - - expect(serializedMetrics['test_name']).toBe(1); - }); - - test('Should convert multiple metrics with the same name and unit into an array', ()=> { - const metrics = new Metrics(); - - metrics.addMetric('test_name', MetricUnits.Count, 2); - metrics.addMetric('test_name', MetricUnits.Count, 1); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(serializedMetrics['test_name']).toStrictEqual([ 2, 1 ]); - }); - - test('Should throw an error if the same metric name is added again with a different unit', ()=> { - expect.assertions(1); - const metrics = new Metrics(); - - metrics.addMetric('test_name', MetricUnits.Count, 2); - try { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - } catch (e) { - expect((e).message).toBe('Metric "test_name" has already been added with unit "Count", but we received unit "Seconds". Did you mean to use metric unit "Count"?'); - } - }); - - test('Should contain multiple metric values if added with multiple names', ()=> { - const metrics = new Metrics(); - - metrics.addMetric('test_name', MetricUnits.Count, 1); - metrics.addMetric('test_name2', MetricUnits.Count, 2); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics).toStrictEqual([ - { Name: 'test_name', Unit: 'Count' }, - { Name: 'test_name2', Unit: 'Count' }, - ]); - - expect(serializedMetrics['test_name']).toBe(1); - expect(serializedMetrics['test_name2']).toBe(2); - }); - }); - - describe('Feature: Resolution of Metrics', ()=>{ - - test('serialized metrics in EMF format should not contain `StorageResolution` as key if none is set', () => { - const metrics = new Metrics(); - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - const serializedMetrics = metrics.serializeMetrics(); - - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).not.toContain('StorageResolution'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Name'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Unit'); - - }); - test('serialized metrics in EMF format should not contain `StorageResolution` as key if `Standard` is set', () => { - const metrics = new Metrics(); - metrics.addMetric('test_name', MetricUnits.Seconds, 10, MetricResolution.Standard); - const serializedMetrics = metrics.serializeMetrics(); - - // expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(MetricResolution.Standard); - // expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(60); - - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).not.toContain('StorageResolution'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Name'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Unit'); - }); - - test('serialized metrics in EMF format should not contain `StorageResolution` as key if `60` is set',()=>{ - const metrics = new Metrics(); - metrics.addMetric('test_name', MetricUnits.Seconds, 10, 60); - const serializedMetrics = metrics.serializeMetrics(); - - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).not.toContain('StorageResolution'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Name'); - expect(Object.keys(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0])).toContain('Unit'); - }); - - test('Should be StorageResolution `1` if MetricResolution is set to `High`',()=>{ - const metrics = new Metrics(); - metrics.addMetric('test_name', MetricUnits.Seconds, 10, MetricResolution.High); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(MetricResolution.High); - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(1); - }); - - test('Should be StorageResolution `1` if MetricResolution is set to `1`',()=>{ - const metrics = new Metrics(); - metrics.addMetric('test_name', MetricUnits.Seconds, 10, 1); - const serializedMetrics = metrics.serializeMetrics(); - - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(MetricResolution.High); - expect(serializedMetrics._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBe(1); - - }); - }); - - describe('Feature: Clearing Metrics ', () => { - test('Clearing metrics should return empty', async () => { - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: { default: 'defaultValue' } }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - const loggedData = metrics.serializeMetrics(); - metrics.clearMetrics(); - const afterClearingLoggedData = metrics.serializeMetrics(); - - expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toEqual(1); - expect(afterClearingLoggedData._aws.CloudWatchMetrics[0].Metrics.length).toEqual(0); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - }); - - test('Publish Stored Metrics should log and clear', async () => { - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name_1', MetricUnits.Count, 1); - metrics.publishStoredMetrics(); - } - } - - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = [ JSON.parse(consoleSpy.mock.calls[0][0]), JSON.parse(consoleSpy.mock.calls[1][0]) ]; - - expect(console.log).toBeCalledTimes(2); - expect(loggedData[0]._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData[1]._aws.CloudWatchMetrics[0].Metrics.length).toBe(0); - }); - - test('Using decorator, it returns a function with the correct scope of the decorated class', async () => { - - // Prepare - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: { default: 'defaultValue' } }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler( - _event: TEvent, - _context: Context): Promise { - this.myMethod(); - - return 'Lambda invoked!'; - } - - private myMethod(): void { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - } - } - - // Act - await new LambdaFunction().handler(event, context); - - // Assess - expect(console.log).toBeCalledTimes(1); - - }); - - test('Using decorator on async handler (without callback) should work fine', async () => { - const metrics = new Metrics({ namespace: 'test' }); - const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics({ defaultDimensions: { default: 'defaultValue' } }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler( - _event: TEvent, - _context: Context): Promise { - metrics.addMetric('test_name', MetricUnits.Seconds, 10); - metrics.addDimension(additionalDimension.name, additionalDimension.value); - const loggedData = metrics.serializeMetrics(); - // Expect the additional dimensions, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); - expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); - metrics.clearDimensions(); - - return 'Lambda invoked!'; - } - } - - await new LambdaFunction().handler(event, context); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - expect(console.log).toBeCalledTimes(1); - // Expect the additional dimension, and the service dimension - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); - expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0]).toContain('default'); - expect(loggedData.default).toContain('defaultValue'); - }); - - test('Using decorator should log even if exception thrown', async () => { - const metrics = new Metrics({ namespace: 'test' }); - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - metrics.addMetric('test_name_1', MetricUnits.Count, 1); - throw new Error('Test Error'); - } - } - - try { - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - } catch (error) { - // DO NOTHING - } - - expect(console.log).toBeCalledTimes(1); - }); - - test('Using decorator should preserve `this` in decorated class', async () => { - // Prepare - const metrics = new Metrics({ namespace: 'test' }); - - // Act - class LambdaFunction implements LambdaInterface { - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - _context: Context, - _callback: Callback, - ): void | Promise { - this.dummyMethod(); - } - - private dummyMethod(): void { - metrics.addMetric('test_name', MetricUnits.Seconds, 1); - } - } - await new LambdaFunction().handler(event, context, () => console.log('Lambda invoked!')); - const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); - - // Assess - expect(console.log).toBeCalledTimes(1); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toEqual(1); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Name).toEqual('test_name'); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].Unit).toEqual('Seconds'); - }); - }); - - describe('Feature: Custom Config Service', () => { - test('Custom Config Service should be called for service', () => { - const serviceName = 'Custom Provider Service Name'; - const namespace = 'Custom Provider namespace'; - const customConfigService = { - getServiceName: () => serviceName, - getNamespace: () => namespace, - }; - - const metrics = new Metrics({ customConfigService: customConfigService }); - const loggedData = metrics.serializeMetrics(); - - expect(loggedData.service).toEqual(serviceName); - expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(namespace); - }); - }); }); From 37adcabb6ad8cab3ac787b73dda0d6163aa2dced Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 15:01:38 +0600 Subject: [PATCH 02/95] test: store metrics --- packages/metrics/tests/unit/Metrics.test.ts | 32 +++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 17ccb06046..5cbc2f2cde 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -4,16 +4,38 @@ * @group unit/metrics/class */ -const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); +import { MetricResolution, MetricUnits, Metrics } from '../../src/'; describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; - beforeEach(() => { - consoleSpy.mockClear(); - }); - beforeAll(() => { process.env = { ...ENVIRONMENT_VARIABLES }; }); + + describe('Method: addMetric', () => { + + test('when called, it should store metrics', () => { + + //Prepare + const metrics = new Metrics(); + const metricName = 'test_metric'; + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 1, MetricResolution.High); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: { + [metricName]: { + name: metricName, + resolution: MetricResolution.High, + unit: MetricUnits.Count, + value: 1 + } + }, + })); + }); + + }); }); From 655b820d7a4d37ed6392a7f6da3dc27f7cd9d664 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 15:03:22 +0600 Subject: [PATCH 03/95] test: create multiple metrics --- packages/metrics/tests/unit/Metrics.test.ts | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 5cbc2f2cde..b612c688e1 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -37,5 +37,39 @@ describe('Class: Metrics', () => { })); }); + test('when called with multiple metric name, it should store multiple metrics', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.addMetric('test_metric-1', MetricUnits.Count, 1, MetricResolution.High); + metrics.addMetric('test_metric-2', MetricUnits.Count, 3, MetricResolution.High); + metrics.addMetric('test_metric-3', MetricUnits.Count, 6, MetricResolution.High); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: { + 'test_metric-1': { + name: 'test_metric-1', + resolution: MetricResolution.High, + unit: MetricUnits.Count, + value: 1 + }, + 'test_metric-2': { + name: 'test_metric-2', + resolution: MetricResolution.High, + unit: MetricUnits.Count, + value: 3 + }, + 'test_metric-3': { + name: 'test_metric-3', + resolution: MetricResolution.High, + unit: MetricUnits.Count, + value: 6 + } + }, + })); + }); }); }); From ebe50f9ff6074960003efe37d639d8eeecde511a Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 21:15:46 +0600 Subject: [PATCH 04/95] test: addMetric default resolution --- packages/metrics/tests/unit/Metrics.test.ts | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index b612c688e1..b2ee0ffe84 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -71,5 +71,33 @@ describe('Class: Metrics', () => { }, })); }); + + test('when called without resolution, it should store metrics with standard resolution', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.addMetric('test-metric-1', MetricUnits.Count, 1); + metrics.addMetric('test-metric-2', MetricUnits.Seconds, 3); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: { + 'test-metric-1': { + name: 'test-metric-1', + resolution: MetricResolution.Standard, + unit: MetricUnits.Count, + value: 1 + }, + 'test-metric-2': { + name: 'test-metric-2', + resolution: MetricResolution.Standard, + unit: MetricUnits.Seconds, + value: 3 + } + }, + })); + }); }); }); From 1d62a1898aed559c8bdd5dba1060220d4ba64fca Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 21:29:39 +0600 Subject: [PATCH 05/95] test: addMetric will group values for same metric name --- packages/metrics/tests/unit/Metrics.test.ts | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index b2ee0ffe84..91ef07d90e 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -99,5 +99,31 @@ describe('Class: Metrics', () => { }, })); }); + + test('when trying to add metric with the same name multiple times, values should be grouped together in an array', () => { + + //Prepare + const metrics = new Metrics(); + const metricName = 'test-metric'; + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.addMetric(metricName, MetricUnits.Count, 5); + metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.addMetric(metricName, MetricUnits.Count, 4); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: { + [metricName]: { + name: metricName, + resolution: MetricResolution.Standard, + unit: MetricUnits.Count, + value: [ 1, 5, 1, 4 ] + } + }, + })); + }); + }); }); From b44a983b96f4f3d795fa738b7b1fa528334f3a07 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 21:42:26 +0600 Subject: [PATCH 06/95] test: addMetric throws error while adding same metric with different unit --- packages/metrics/tests/unit/Metrics.test.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 91ef07d90e..d8967dd4e6 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -105,7 +105,7 @@ describe('Class: Metrics', () => { //Prepare const metrics = new Metrics(); const metricName = 'test-metric'; - + //Act metrics.addMetric(metricName, MetricUnits.Count, 1); metrics.addMetric(metricName, MetricUnits.Count, 5); @@ -125,5 +125,19 @@ describe('Class: Metrics', () => { })); }); + test('when trying to add metric with the same name multiple times but with different unit, it will throw an error', () => { + + //Prepare + const metrics = new Metrics(); + const metricName = 'test-metric'; + + // Act & Assess + expect(() => { + metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.addMetric(metricName, MetricUnits.Kilobits, 5); + }).toThrowError(Error); + + }); + }); }); From 4986d6d5d7c3967307f0ddba70f6eb71af2761fc Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 22:00:09 +0600 Subject: [PATCH 07/95] test: addMetric will publish metrics if stored metrics count has reached max metric size threshold --- packages/metrics/tests/unit/Metrics.test.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d8967dd4e6..7cdad94288 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -136,8 +136,24 @@ describe('Class: Metrics', () => { metrics.addMetric(metricName, MetricUnits.Count, 1); metrics.addMetric(metricName, MetricUnits.Kilobits, 5); }).toThrowError(Error); - + }); + test('it will publish metrics if stored metrics count has reached max metric size threshold', () => { + + //Prepare + const metrics = new Metrics(); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + const metricName = 'test-metric'; + + //Act + for (let i = 0; i <= 100; i++) { + metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + } + + // Assess + expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(1); + + }); }); }); From 3ec7e74c0a7d9a741019fa50a71d9a4140da7d80 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 22:01:32 +0600 Subject: [PATCH 08/95] test: addMetric will not publish if stored metrics count has not reached max metric size threshold --- packages/metrics/tests/unit/Metrics.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 7cdad94288..202c2936d3 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -155,5 +155,22 @@ describe('Class: Metrics', () => { expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(1); }); + + test('it will not publish metrics if stored metrics count has not reached max metric size threshold', () => { + + //Prepare + const metrics = new Metrics(); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + const metricName = 'test-metric'; + + //Act + for (let i = 0; i < 100; i++) { + metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + } + + // Assess + expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); + + }); }); }); From 1e3845a98c371a4524c314290cdfc6b6f793ed8e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 26 Mar 2023 22:07:52 +0600 Subject: [PATCH 09/95] test: addDimension storing dimension --- packages/metrics/tests/unit/Metrics.test.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 202c2936d3..12447a1a50 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -173,4 +173,26 @@ describe('Class: Metrics', () => { }); }); + + describe('Method: addDimension', () => { + + test('when called, it should store dimensions', () => { + + //Prepare + const metrics = new Metrics(); + const dimensionName = 'test-dimension'; + const dimensionValue= 'test-value'; + + //Act + metrics.addDimension(dimensionName, dimensionValue); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: { + [dimensionName]: dimensionValue + }, + })); + + }); + }); }); From 66b9a444c3865c34d3d076c21e7504cd720a7999 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 21:41:59 +0600 Subject: [PATCH 10/95] test: addDimension giving error if number of dimensions exceeds the maximum allowed --- packages/metrics/tests/unit/Metrics.test.ts | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 12447a1a50..dadf1692ff 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -194,5 +194,38 @@ describe('Class: Metrics', () => { })); }); + + test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + + //Prepare + const metrics = new Metrics(); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + + // Act & Assess + expect(() => { + for (let i = 0; i < 29; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).toThrowError(RangeError); + + }); + + test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { + + //Prepare + const metrics = new Metrics({ defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + + // Act & Assess + expect(() => { + for (let i = 0; i < 27; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).toThrowError(RangeError); + + }); + }); }); From 61b7da91b286001b6ed4f03f71cfc449ff85750c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 21:44:27 +0600 Subject: [PATCH 11/95] test: addDimensions should add multiple dimensions --- packages/metrics/tests/unit/Metrics.test.ts | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index dadf1692ff..c73ddc5932 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -228,4 +228,27 @@ describe('Class: Metrics', () => { }); }); + + describe('Method: addDimensions', () => { + + test('it should add multiple dimensions', () => { + + //Prepare + const dimensionsToBeAdded: { [key: string]: string } = { + 'test-dimension-1': 'test-value-1', + 'test-dimension-2': 'test-value-2', + }; + const metrics = new Metrics(); + + //Act + metrics.addDimensions(dimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: dimensionsToBeAdded + })); + + }); + + }); }); From 78705eac6f7292dd0dca56a994a776aec49cf009 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 21:51:21 +0600 Subject: [PATCH 12/95] test: addDimensions should update existing dimension value if added again --- packages/metrics/tests/unit/Metrics.test.ts | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index c73ddc5932..60085c191b 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -249,6 +249,29 @@ describe('Class: Metrics', () => { })); }); + + test('if same dimension is added again, it should update existing dimension value', () => { + + //Prepare + const dimensionsToBeAdded: { [key: string]: string } = { + 'test-dimension-1': 'test-value-1', + 'test-dimension-2': 'test-value-2', + }; + const metrics = new Metrics(); + + //Act + metrics.addDimensions(dimensionsToBeAdded); + metrics.addDimensions({ 'test-dimension-1': 'test-value-3' }); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: { + 'test-dimension-1': 'test-value-3', + 'test-dimension-2': 'test-value-2', + } + })); + + }); }); }); From 0f088060968a4d0882d6747cea3ad26263d404f6 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:15:04 +0600 Subject: [PATCH 13/95] test: addDimensions should throw error if maximum dimension count crosses --- packages/metrics/tests/unit/Metrics.test.ts | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 60085c191b..3389a40ffe 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -272,6 +272,43 @@ describe('Class: Metrics', () => { })); }); + + test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + + //Prepare + const metrics = new Metrics(); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const dimensionsToBeAdded: { [key: string]: string } = {}; + for (let i = 0; i <= 29; i++) { + dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.addDimensions(dimensionsToBeAdded); + }).toThrowError(RangeError); + + }); + + test('it should successfully add up to maximum allowed dimensions without throwing error', () => { + + //Prepare + const metrics = new Metrics(); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const dimensionsToBeAdded: { [key: string]: string } = {}; + for (let i = 0; i < 29; i++) { + dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.addDimensions(dimensionsToBeAdded); + }).not.toThrowError(RangeError); + expect(metrics).toEqual(expect.objectContaining({ dimensions: dimensionsToBeAdded })); + + }); }); }); From 649dc5b1d7533ad51042c24c806367be3cf66f17 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:19:14 +0600 Subject: [PATCH 14/95] test: addMetadata should add metadata --- packages/metrics/tests/unit/Metrics.test.ts | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 3389a40ffe..33c6525ed2 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -311,4 +311,39 @@ describe('Class: Metrics', () => { }); }); + + describe('Method: addMetadata', () => { + + test('it should add metadata', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.addMetadata('foo', 'bar'); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + metadata: { 'foo': 'bar' } + })); + + }); + + test('it should update metadata value if added again', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.addMetadata('foo', 'bar'); + metrics.addMetadata('foo', 'baz'); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + metadata: { 'foo': 'baz' } + })); + + }); + }); + }); From 99c447a97b53619e970497c3684ac6ecd5f96036 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:27:39 +0600 Subject: [PATCH 15/95] test: clearDimensions should clear dimensions --- packages/metrics/tests/unit/Metrics.test.ts | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 33c6525ed2..5c742bf877 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -346,4 +346,38 @@ describe('Class: Metrics', () => { }); }); + describe('Method: clearDimensions', () => { + + test('it should clear all dimensions', () => { + + //Prepare + const metrics = new Metrics(); + metrics.addDimension('foo', 'bar'); + + //Act + metrics.clearDimensions(); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: {} + })); + + }); + + test('it should not clear default dimensions', () => { + + //Prepare + const metrics = new Metrics({ defaultDimensions: { 'environment': 'prod' } }); + + //Act + metrics.clearDimensions(); + + // Assess + expect(metrics).not.toEqual(expect.objectContaining({ + defaultDimensions: {} + })); + + }); + }); + }); From 6541fba2bad2e2e2356fc5852b935349a0e41033 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:29:47 +0600 Subject: [PATCH 16/95] test: clearMetadata should clear all metadata --- packages/metrics/tests/unit/Metrics.test.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 5c742bf877..63d50759ab 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -378,6 +378,28 @@ describe('Class: Metrics', () => { })); }); + + }); + + describe('Method: clearMetadata', () => { + + test('it should clear all metadata', () => { + + //Prepare + const metrics = new Metrics(); + metrics.addMetadata('foo', 'bar'); + metrics.addMetadata('test', 'baz'); + + //Act + metrics.clearMetadata(); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + metadata: {} + })); + + }); + }); }); From d789b4e142fc0b4a6175aaf436cd1d2eaef7fdfb Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:32:11 +0600 Subject: [PATCH 17/95] test: clearMetrics should clear all stored metrics --- packages/metrics/tests/unit/Metrics.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 63d50759ab..ddd56ffabb 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -174,6 +174,27 @@ describe('Class: Metrics', () => { }); }); + describe('Method: clearMetrics', () => { + + test('when called, it should clear stored metrics', () => { + + //Prepare + const metrics = new Metrics(); + const metricName = 'test-metric'; + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.clearMetrics(); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: {}, + })); + + }); + + }); + describe('Method: addDimension', () => { test('when called, it should store dimensions', () => { From f01b6ae2d369b9dee855133390f1374a7b30e65e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Wed, 29 Mar 2023 22:55:48 +0600 Subject: [PATCH 18/95] test: setDefaultDimensions should set default dimensions object --- packages/metrics/tests/unit/Metrics.test.ts | 83 +++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index ddd56ffabb..338d69c302 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -333,6 +333,89 @@ describe('Class: Metrics', () => { }); + describe('Method: setDefaultDimensions', () => { + + test('it should set default dimensions when service name is not provided', () => { + + //Prepare + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const metrics = new Metrics(); + + //Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : 'service_undefined' } + })); + + }); + + test('it should set default dimensions when service name is provided', () => { + + //Prepare + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const serviceName = 'test-service'; + const metrics = new Metrics({ serviceName: serviceName }); + + //Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName } + })); + + }); + + test('it should add default dimensions', () => { + + //Prepare + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const serviceName = 'test-service'; + const metrics = new Metrics({ serviceName: serviceName , defaultDimensions: { 'test-dimension': 'test-dimension-value' } }); + + //Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName , 'test-dimension': 'test-dimension-value' } + })); + + }); + + test('it should update already added default dimensions values', () => { + + //Prepare + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const serviceName = 'test-service'; + const metrics = new Metrics({ serviceName: serviceName, defaultDimensions: { 'environment': 'dev' } }); + + //Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { foo: 'bar', service: serviceName, 'environment': 'prod' } + })); + + }); + + }); + describe('Method: addMetadata', () => { test('it should add metadata', () => { From 07dd43ba73de0f1093b21f81592aec9527f5e7cb Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 30 Mar 2023 21:02:37 +0600 Subject: [PATCH 19/95] test: setDefaultDimensions should throw error if default dimensions reaches maximum allowed --- packages/metrics/tests/unit/Metrics.test.ts | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 338d69c302..04b5e8493b 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -413,6 +413,47 @@ describe('Class: Metrics', () => { })); }); + + test('it should throw error if number of dimensions reaches the maximum allowed', () => { + + //Prepare + const metrics = new Metrics(); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const defaultDimensions: { [key: string]: string } = {}; + for (let i = 0; i <= 29; i++) { + defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.setDefaultDimensions(defaultDimensions); + }).toThrowError(Error); + + }); + + test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { + + //Prepare + const metrics = new Metrics({ + defaultDimensions: { + 'test-dimension': 'test-value', + 'environment': 'dev' + } + }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const defaultDimensions: { [key: string]: string } = {}; + for (let i = 0; i < 27; i++) { + defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.setDefaultDimensions(defaultDimensions); + }).toThrowError(Error); + + }); }); From 8b4c92b2b38cf3788889742aa540c82b2c8eb16f Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 30 Mar 2023 21:06:02 +0600 Subject: [PATCH 20/95] test: clearDefaultDimensions should clear all default dimensions --- packages/metrics/tests/unit/Metrics.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 04b5e8493b..4d4bf979e6 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -457,6 +457,25 @@ describe('Class: Metrics', () => { }); + describe('Method: clearDefaultDimensions', () => { + + test('it should clear all default dimensions', () => { + + //Prepare + const metrics = new Metrics(); + metrics.setDefaultDimensions({ 'foo': 'bar' }); + + //Act + metrics.clearDefaultDimensions(); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: {} + })); + + }); + }); + describe('Method: addMetadata', () => { test('it should add metadata', () => { From 3e63b194884747e50b69ea907e3498ebd90f16be Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 30 Mar 2023 21:42:25 +0600 Subject: [PATCH 21/95] test: singleMetric should return a single metric object --- packages/metrics/tests/unit/Metrics.test.ts | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 4d4bf979e6..3fded6d158 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -566,4 +566,34 @@ describe('Class: Metrics', () => { }); + describe('Method: singleMetric', () => { + + test('it should return a single Metric object', () => { + + //Prepare + const namespace = 'test-namespace'; + const defaultDimensions = { + 'foo': 'bar', + 'service': 'order' + }; + const metrics = new Metrics({ + namespace, + defaultDimensions, + singleMetric: false + }); + + //Act + const singleMetric = metrics.singleMetric(); + + //Asses + expect(singleMetric).toEqual(expect.objectContaining({ + isSingleMetric: true, + namespace, + defaultDimensions + })); + + }); + + }); + }); From 0d06a8d135f34e846fae617fc98a6c68399efd86 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 1 Apr 2023 13:40:22 +0600 Subject: [PATCH 22/95] test: throwOnEmptyMetrics will set the throwOnEmptyMetrics flag to true --- packages/metrics/tests/unit/Metrics.test.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 3fded6d158..7977f2ced6 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -593,7 +593,26 @@ describe('Class: Metrics', () => { })); }); - + + }); + + describe('Method: throwOnEmptyMetrics', () => { + + test('it should set the throwOnEmptyMetrics flag to true', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.throwOnEmptyMetrics(); + + //Assess + expect(metrics).toEqual(expect.objectContaining({ + shouldThrowOnEmptyMetrics: true + })); + + }); + }); }); From 30fb771060c07670480a581867226c823e16c09c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 1 Apr 2023 13:42:30 +0600 Subject: [PATCH 23/95] test: setFunctionName should set the function name --- packages/metrics/tests/unit/Metrics.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 7977f2ced6..cef85fcaff 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -615,4 +615,23 @@ describe('Class: Metrics', () => { }); + describe('Method: setFunctionName', () => { + + test('it should set the function name', () => { + + //Prepare + const metrics = new Metrics(); + + //Act + metrics.setFunctionName('test-function'); + + //Assess + expect(metrics).toEqual(expect.objectContaining({ + functionName: 'test-function' + })); + + }); + + }); + }); From ce11baec94d6973af0096ce3bd78836e51fd2a3c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 15:53:46 +0600 Subject: [PATCH 24/95] test: logMetrics should log metrics --- packages/metrics/tests/unit/Metrics.test.ts | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index cef85fcaff..34fbcee299 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -4,10 +4,18 @@ * @group unit/metrics/class */ +import { + LambdaInterface, + ContextExamples as dummyContext, + Events as dummyEvent +} from '@aws-lambda-powertools/commons'; import { MetricResolution, MetricUnits, Metrics } from '../../src/'; +import { Context } from 'aws-lambda'; describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; + const context = dummyContext.helloworldContext; + const event = dummyEvent.Custom.CustomEvent; beforeAll(() => { process.env = { ...ENVIRONMENT_VARIABLES }; @@ -634,4 +642,42 @@ describe('Class: Metrics', () => { }); + describe('Method: logMetrics', () => { + + const expectedReturnValue = 'Lambda invoked!'; + const testMetric = 'successfulBooking'; + + test('it should log metrics', async () => { + + //Prepare + const metrics = new Metrics(); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + const addMetricSpy = jest.spyOn(metrics, 'addMetric'); + const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); + class LambdaFunction implements LambdaInterface { + + @metrics.logMetrics() + public async handler(_event: TEvent, _context: Context): Promise { + metrics.addMetric(testMetric, MetricUnits.Count, 1); + + return expectedReturnValue; + } + + } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + // Act + const actualResult = await handler(event, context); + + // Assess + expect(actualResult).toEqual(expectedReturnValue); + expect(captureColdStartMetricSpy).not.toBeCalled(); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(publishStoredMetricsSpy).toBeCalledTimes(1); + + }); + + }); + }); From 4bde67d090e9477db2b3194a4d2d0b7fceab9d54 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 15:54:10 +0600 Subject: [PATCH 25/95] test: logMetrics should capture cold start metrics --- packages/metrics/tests/unit/Metrics.test.ts | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 34fbcee299..f326e890fe 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -678,6 +678,37 @@ describe('Class: Metrics', () => { }); + test('it should capture cold start metrics, if passed in the options as true', async () => { + + //Prepare + const metrics = new Metrics(); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + const addMetricSpy = jest.spyOn(metrics, 'addMetric'); + const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); + class LambdaFunction implements LambdaInterface { + + @metrics.logMetrics({ captureColdStartMetric: true }) + public async handler(_event: TEvent, _context: Context): Promise { + metrics.addMetric(testMetric, MetricUnits.Count, 1); + + return expectedReturnValue; + } + + } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + // Act + const actualResult = await handler(event, context); + + // Assess + expect(actualResult).toEqual(expectedReturnValue); + expect(captureColdStartMetricSpy).toBeCalledTimes(1); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(publishStoredMetricsSpy).toBeCalledTimes(1); + + }); + }); }); From 32bce7aab890e2f65df20e46481c6f595f5ab82f Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 15:57:21 +0600 Subject: [PATCH 26/95] test: logMetrics should throw error if no metrics are added and throwOnEmptyMetrics is set to true --- packages/metrics/tests/unit/Metrics.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index f326e890fe..36fa80f0c0 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -709,6 +709,26 @@ describe('Class: Metrics', () => { }); + test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { + + //Prepare + const metrics = new Metrics(); + class LambdaFunction implements LambdaInterface { + + @metrics.logMetrics({ throwOnEmptyMetrics: true }) + public async handler(_event: TEvent, _context: Context): Promise { + return expectedReturnValue; + } + + } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + // Act & Assess + await expect(handler(event, context)).rejects.toThrowError(RangeError); + + }); + }); }); From 20a9e5386be6051811426456bd2ed595189a9036 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 16:00:32 +0600 Subject: [PATCH 27/95] test: logMetrics should set default dimensions if passed in the options --- packages/metrics/tests/unit/Metrics.test.ts | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 36fa80f0c0..42b5ecf0cf 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -729,6 +729,41 @@ describe('Class: Metrics', () => { }); + test('it should set default dimensions if passed in the options', async () => { + + //Prepare + const defaultDimensions = { + 'foo': 'bar', + 'service': 'order' + }; + const metrics = new Metrics(); + const setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + const addMetricSpy = jest.spyOn(metrics, 'addMetric'); + + class LambdaFunction implements LambdaInterface { + + @metrics.logMetrics({ defaultDimensions }) + public async handler(_event: TEvent, _context: Context): Promise { + metrics.addMetric(testMetric, MetricUnits.Count, 1); + + return expectedReturnValue; + } + + } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + // Act + await handler(event, context); + + // Assess + expect(setDefaultDimensionsSpy).toHaveBeenNthCalledWith(1, defaultDimensions); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(publishStoredMetricsSpy).toBeCalledTimes(1); + + }); + }); }); From 40817936f37d58b35831c684e457e9ffb74a7d8c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 16:02:41 +0600 Subject: [PATCH 28/95] test: logMetrics should throw error if lambda handler throws any error --- packages/metrics/tests/unit/Metrics.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 42b5ecf0cf..5ccd877087 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -764,6 +764,27 @@ describe('Class: Metrics', () => { }); + test('it should throw error if lambda handler throws any error', async () => { + + //Prepare + const metrics = new Metrics(); + const errorMessage = 'Unexpected error occurred!'; + class LambdaFunction implements LambdaInterface { + + @metrics.logMetrics() + public async handler(_event: TEvent, _context: Context): Promise { + throw new Error(errorMessage); + } + + } + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + // Act & Assess + await expect(handler(event, context)).rejects.toThrowError(errorMessage); + + }); + }); }); From 12a3a529e36193bb0bbe2f2f02ede85f6ed50037 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 2 Apr 2023 16:21:44 +0600 Subject: [PATCH 29/95] test: serializeMetrics should print warning, if no namespace provided in constructor or environment variable --- packages/metrics/tests/unit/Metrics.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 5ccd877087..895c817f18 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -787,4 +787,23 @@ describe('Class: Metrics', () => { }); + describe('Method: serializeMetrics', () => { + + test('it should print warning, if no namespace provided in constructor or environment variable', () => { + + //Prepare + process.env.POWERTOOLS_METRICS_NAMESPACE = ''; + const metrics = new Metrics(); + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + + //Act + metrics.serializeMetrics(); + + //Assess + expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); + + }); + + }); + }); From 94c0c1ad69a05870b5c7f69788682492767e595b Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 3 Apr 2023 20:41:30 +0600 Subject: [PATCH 30/95] feat: metrics helper --- packages/metrics/src/helpers.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/metrics/src/helpers.ts diff --git a/packages/metrics/src/helpers.ts b/packages/metrics/src/helpers.ts new file mode 100644 index 0000000000..16a9c7f2a8 --- /dev/null +++ b/packages/metrics/src/helpers.ts @@ -0,0 +1,8 @@ +import { MetricsOptions } from 'types'; +import { Metrics } from '.'; + +const createMetrics = (options: MetricsOptions = {}): Metrics => new Metrics(options); + +export { + createMetrics, +}; \ No newline at end of file From 94c8a398bc3829b4db2052ca5ac7c575dcad185e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 3 Apr 2023 21:05:23 +0600 Subject: [PATCH 31/95] test: createMetrics should return appropriate values for no constructor params and no environment variables --- packages/metrics/src/index.ts | 1 + packages/metrics/tests/unit/helpers.test.ts | 83 +++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 packages/metrics/tests/unit/helpers.test.ts diff --git a/packages/metrics/src/index.ts b/packages/metrics/src/index.ts index ffa141c1e6..8002a99314 100644 --- a/packages/metrics/src/index.ts +++ b/packages/metrics/src/index.ts @@ -1,3 +1,4 @@ +export * from './helpers'; export * from './Metrics'; export * from './MetricsInterface'; export * from './middleware'; \ No newline at end of file diff --git a/packages/metrics/tests/unit/helpers.test.ts b/packages/metrics/tests/unit/helpers.test.ts new file mode 100644 index 0000000000..176ac3a1bb --- /dev/null +++ b/packages/metrics/tests/unit/helpers.test.ts @@ -0,0 +1,83 @@ +/** + * Test Metrics helpers + * + * @group unit/metrics/all + */ +import { createMetrics, Metrics } from '../../src'; +import { EnvironmentVariablesService } from '../../src/config'; + +describe('Helper: createMetrics function', () => { + + const ENVIRONMENT_VARIABLES = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...ENVIRONMENT_VARIABLES }; + }); + + afterAll(() => { + process.env = ENVIRONMENT_VARIABLES; + }); + + describe('MetricsOptions constructor parameters', () => { + + test('when no constructor parameters are set, returns a Metrics instance with the options set in the environment variables', () => { + + // Prepare + const metricsOptions = undefined; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when no constructor parameters and no environment variables are set, returns a Metrics instance with the default properties', () => { + + // Prepare + const metricsOptions = undefined; + process.env = {}; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: '', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + }); + +}); \ No newline at end of file From 1a9ed4ca58e7be1b7c20d956d895530ba7a5800b Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 3 Apr 2023 21:45:05 +0600 Subject: [PATCH 32/95] test: createMetrics should return appropriate instance when constructor params are set --- packages/metrics/tests/unit/helpers.test.ts | 198 +++++++++++++++++++- 1 file changed, 197 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/helpers.test.ts b/packages/metrics/tests/unit/helpers.test.ts index 176ac3a1bb..8610828d7d 100644 --- a/packages/metrics/tests/unit/helpers.test.ts +++ b/packages/metrics/tests/unit/helpers.test.ts @@ -4,7 +4,8 @@ * @group unit/metrics/all */ import { createMetrics, Metrics } from '../../src'; -import { EnvironmentVariablesService } from '../../src/config'; +import { ConfigServiceInterface, EnvironmentVariablesService } from '../../src/config'; +import { MetricsOptions } from '../../src/types'; describe('Helper: createMetrics function', () => { @@ -77,6 +78,201 @@ describe('Helper: createMetrics function', () => { })); }); + + test('when constructor parameters are set, returns a Metrics instance with the options set in the constructor parameters', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + customConfigService: new EnvironmentVariablesService(), + namespace: 'test-namespace', + serviceName: 'test-service', + singleMetric: true, + defaultDimensions: { + service: 'order', + }, + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: expect.any(EnvironmentVariablesService), + defaultDimensions: metricsOptions.defaultDimensions, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: true, + metadata: {}, + namespace: metricsOptions.namespace, + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + }); + + test('when custom namespace is passed, returns a Metrics instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + namespace: 'test-namespace', + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: metricsOptions.namespace, + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom defaultDimensions is passed, returns a Metrics instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + defaultDimensions: { + service: 'order', + }, + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: metricsOptions.defaultDimensions, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when singleMetric is passed, returns a Metrics instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + singleMetric: true, + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: true, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom customConfigService is passed, returns a Metrics instance with the correct properties', () => { + + // Prepare + const configService: ConfigServiceInterface = { + get(name: string): string { + return `a-string-from-${name}`; + }, + getNamespace(): string{ + return 'test-namespace'; + }, + getServiceName(): string{ + return 'test-service'; + } + }; + const metricsOptions: MetricsOptions = { + customConfigService: configService, + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: configService, + defaultDimensions: { + service: 'test-service' + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'test-namespace', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom serviceName is passed, returns a Metrics instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + serviceName: 'test-service', + }; + + // Act + const metrics = createMetrics(metricsOptions); + + // Assess + expect(metrics).toBeInstanceOf(Metrics); + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'test-service' + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); }); From c1175b75859203224573aad9f24e7adcce40a339 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Fri, 7 Apr 2023 20:15:44 +0600 Subject: [PATCH 33/95] test: serializeMetricscreateMetrics should return right object compliant with Cloudwatch EMF --- packages/metrics/tests/unit/Metrics.test.ts | 59 ++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 895c817f18..24a3a21bab 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -9,15 +9,19 @@ import { ContextExamples as dummyContext, Events as dummyEvent } from '@aws-lambda-powertools/commons'; -import { MetricResolution, MetricUnits, Metrics } from '../../src/'; +import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; +const mockDate = new Date(1466424490000); +const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); + describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; const context = dummyContext.helloworldContext; const event = dummyEvent.Custom.CustomEvent; beforeAll(() => { + dateSpy.mockClear(); process.env = { ...ENVIRONMENT_VARIABLES }; }); @@ -804,6 +808,59 @@ describe('Class: Metrics', () => { }); + test('it should return right object compliant with Cloudwatch EMF', () => { + + //Prepare + const metrics: Metrics = createMetrics({ + namespace: 'test-namespace', + serviceName: 'test-service', + defaultDimensions: { + 'environment': 'dev' + } + }); + + //Act + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnits.Count, 3); + metrics.addMetric('failedBooking', MetricUnits.Count, 1, MetricResolution.High); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData).toEqual( + { + '_aws': { + 'Timestamp': mockDate.getTime(), + 'CloudWatchMetrics': [ + { + 'Namespace': 'test-namespace', + 'Dimensions': [ + [ + 'service', + 'environment' + ] + ], + 'Metrics': [ + { + 'Name': 'successfulBooking', + 'Unit': 'Count' + }, + { + 'Name': 'failedBooking', + 'Unit': 'Count', + 'StorageResolution': 1 + } + ] + } + ] + }, + 'environment': 'dev', + 'service': 'test-service', + 'successfulBooking': [ 1, 3 ], + 'failedBooking': 1 + } + ); + }); + }); }); From bed96a920aeee131b278361dc5c38aa2038cc8b7 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 12:10:43 +0600 Subject: [PATCH 34/95] test: serializeMetricscreateMetrics should log service dimensions correctly --- packages/metrics/tests/unit/Metrics.test.ts | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 24a3a21bab..d288db9934 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -861,6 +861,37 @@ describe('Class: Metrics', () => { ); }); + test('it should log service dimension correctly when passed', () => { + + //Prepare + const serviceName = 'test-service'; + const metrics: Metrics = createMetrics({ serviceName:serviceName }); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData.service).toEqual(serviceName); + + }); + + test('it should log service dimension correctly from env var when not passed', () => { + + //Prepare + const serviceName = 'hello-world-service'; + process.env.POWERTOOLS_SERVICE_NAME = serviceName; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData.service).toEqual(serviceName); + + }); + }); }); From 427637dfa34a5675bc4bd17125c03e1fae1611e3 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 12:36:29 +0600 Subject: [PATCH 35/95] test: serializeMetricscreateMetrics should log other dimensions correctly --- packages/metrics/tests/unit/Metrics.test.ts | 65 +++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d288db9934..e9668581cd 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -793,6 +793,8 @@ describe('Class: Metrics', () => { describe('Method: serializeMetrics', () => { + const defaultServiceName = 'service_undefined'; + test('it should print warning, if no namespace provided in constructor or environment variable', () => { //Prepare @@ -889,9 +891,72 @@ describe('Class: Metrics', () => { //Assess expect(loggedData.service).toEqual(serviceName); + delete process.env.POWERTOOLS_SERVICE_NAME; }); + test('it should log default dimensions correctly', () => { + + //Prepare + const additionalDimensions = { + 'foo': 'bar', + 'env': 'dev' + }; + const metrics: Metrics = createMetrics({ defaultDimensions: additionalDimensions }); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); + expect(loggedData.service).toEqual(defaultServiceName); + expect(loggedData.foo).toEqual(additionalDimensions.foo); + expect(loggedData.env).toEqual(additionalDimensions.env); + + }); + + test('it should log additional dimensions correctly', () => { + + //Prepare + const additionalDimension = { name: 'metric2', value: 'metric2Value' }; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); + metrics.addDimension(additionalDimension.name, additionalDimension.value); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); + expect(loggedData.service).toEqual(defaultServiceName); + expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); + + }); + + test('it should log additional bulk dimensions correctly', () => { + + //Prepare + const additionalDimensions: { [key: string]: string } = { + metric2: 'metric2Value', + metric3: 'metric3Value' + }; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); + metrics.addDimensions(additionalDimensions); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); + expect(loggedData.service).toEqual(defaultServiceName); + Object.keys(additionalDimensions).forEach((key) => { + expect(loggedData[key]).toEqual(additionalDimensions[key]); + }); + + }); + }); }); From 61a8c9e6db7784c2e1209f55d052e74504c54d87 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 12:51:17 +0600 Subject: [PATCH 36/95] refactor: clearDimensions should not clear default dimensions --- packages/metrics/tests/unit/Metrics.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index e9668581cd..263e33ffba 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -544,13 +544,18 @@ describe('Class: Metrics', () => { //Prepare const metrics = new Metrics({ defaultDimensions: { 'environment': 'prod' } }); + metrics.addDimension('foo', 'bar'); //Act metrics.clearDimensions(); // Assess - expect(metrics).not.toEqual(expect.objectContaining({ - defaultDimensions: {} + expect(metrics).toEqual(expect.objectContaining({ + dimensions: {}, + defaultDimensions: { + 'environment': 'prod', + 'service': 'service_undefined' + } })); }); From 91e5214569ab777fcd7b153ea8d6cd5ab0af010c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 12:52:18 +0600 Subject: [PATCH 37/95] test: serializeMetrics should log metadata correctly --- packages/metrics/tests/unit/Metrics.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 263e33ffba..d2a44f73cc 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -962,6 +962,21 @@ describe('Class: Metrics', () => { }); + test('it should log metadata correctly', () => { + + //Prepare + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetadata('foo', 'bar'); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData.foo).toEqual('bar'); + + }); + }); }); From 2c83ca905d520cfddd568f9be5f11dba5ad27738 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 15:12:14 +0600 Subject: [PATCH 38/95] test: serializeMetrics should throw error on empty metrics when throwOnEmptyMetrics is true --- packages/metrics/tests/unit/Metrics.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d2a44f73cc..544b626246 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -977,6 +977,19 @@ describe('Class: Metrics', () => { }); + test('it should throw error on empty metrics when throwOnEmptyMetrics is true', () => { + + //Prepare + const metrics: Metrics = createMetrics(); + + //Act + metrics.throwOnEmptyMetrics(); + + //Assess + expect(() => metrics.serializeMetrics()).toThrow('The number of metrics recorded must be higher than zero'); + + }); + }); }); From 83c5972c747ab6c818c4ece27959e87857e4cae8 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 15:16:49 +0600 Subject: [PATCH 39/95] test: serializeMetrics should log namespaces properly --- packages/metrics/tests/unit/Metrics.test.ts | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 544b626246..2b538ee185 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -12,6 +12,7 @@ import { import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; +const DEFAULT_NAMESPACE = 'default_namespace'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -990,6 +991,35 @@ describe('Class: Metrics', () => { }); + test('it should use default namespace when not provided', () => { + + //Prepare + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(DEFAULT_NAMESPACE); + + }); + + test('it should use namespace provided in constructor', () => { + + //Prepare + const namespace = 'test-namespace'; + const metrics: Metrics = createMetrics({ namespace: namespace }); + + //Act + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(namespace); + + }); + }); }); From 13f552f9df0cd036c6629d0dd6afe59aff1da577 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 15:34:44 +0600 Subject: [PATCH 40/95] test: serializeMetrics should log metric values properly --- packages/metrics/tests/unit/Metrics.test.ts | 52 +++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 2b538ee185..f8553b77d2 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1020,6 +1020,58 @@ describe('Class: Metrics', () => { }); + test('it should contain a metric value if added once', () => { + + //Prepare + const metricName = 'test-metrics'; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); + expect(loggedData['test-metrics']).toEqual(10); + + }); + + test('it should convert metric value with the same name and unit to array if added multiple times', () => { + + //Prepare + const metricName = 'test-metrics'; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 10); + metrics.addMetric(metricName, MetricUnits.Count, 20); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); + expect(loggedData[metricName]).toEqual([ 10, 20 ]); + + }); + + test('it should create multiple metric values if added multiple times', () => { + + //Prepare + const metricName1 = 'test-metrics'; + const metricName2 = 'test-metrics-2'; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric(metricName1, MetricUnits.Count, 10); + metrics.addMetric(metricName2, MetricUnits.Seconds, 20); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); + expect(loggedData[metricName1]).toEqual(10); + expect(loggedData[metricName2]).toEqual(20); + + }); + }); }); From a8704748da00fb7ddadf9cdc23fc92a8f01533ce Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 15:49:05 +0600 Subject: [PATCH 41/95] test: serializeMetrics should log properly --- packages/metrics/tests/unit/Metrics.test.ts | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index f8553b77d2..3f657cc0df 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1072,6 +1072,41 @@ describe('Class: Metrics', () => { }); + test('it should not contain `StorageResolution` as key for non-high resolution metrics', () => { + + //Prepare + const metricName = 'test-metrics'; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 10); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); + expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); + + }); + + test('it should contain `StorageResolution` as key & high metric resolution as value for high resolution metrics', () => { + + //Prepare + const metricName = 'test-metrics'; + const metricName2 = 'test-metrics-2'; + const metrics: Metrics = createMetrics(); + + //Act + metrics.addMetric(metricName, MetricUnits.Count, 10); + metrics.addMetric(metricName2, MetricUnits.Seconds, 10, MetricResolution.High); + const loggedData = metrics.serializeMetrics(); + + //Assess + expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); + expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); + expect(loggedData._aws.CloudWatchMetrics[0].Metrics[1].StorageResolution).toEqual(MetricResolution.High); + + }); + }); }); From ca7a04b934ae5e173da7085fb04940dfffcb4a5e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 22:13:43 +0600 Subject: [PATCH 42/95] test: serializeMetrics should show warning if no metrics are added --- packages/metrics/tests/unit/Metrics.test.ts | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 3f657cc0df..d0482347b8 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -21,6 +21,10 @@ describe('Class: Metrics', () => { const context = dummyContext.helloworldContext; const event = dummyEvent.Custom.CustomEvent; + beforeEach(() => { + jest.clearAllMocks(); + }); + beforeAll(() => { dateSpy.mockClear(); process.env = { ...ENVIRONMENT_VARIABLES }; @@ -1109,4 +1113,24 @@ describe('Class: Metrics', () => { }); + describe('Methods: publishStoredMetrics', () => { + + test('it should console warning if no metrics are added', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(consoleWarnSpy).toBeCalledTimes(1); + expect(consoleWarnSpy).toBeCalledWith( + 'No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using \'throwOnEmptyMetrics\'', + ); + + }); + }); + }); From 1a25d5cf4e9cf8f5ec6afbf3d09f0db1cb90de1b Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 8 Apr 2023 22:48:44 +0600 Subject: [PATCH 43/95] test: serializeMetrics should should call serializeMetrics && log the stringified return value of serializeMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d0482347b8..6a7a8489d7 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -11,6 +11,7 @@ import { } from '@aws-lambda-powertools/commons'; import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; +import { EmfOutput } from '../../src/types'; const DEFAULT_NAMESPACE = 'default_namespace'; const mockDate = new Date(1466424490000); @@ -1131,6 +1132,47 @@ describe('Class: Metrics', () => { ); }); + + test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); + const mockData: EmfOutput = { + '_aws': { + 'Timestamp': 1466424490000, + 'CloudWatchMetrics': [ + { + 'Namespace': 'test', + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': 'test-metrics', + 'Unit': MetricUnits.Count + } + ] + } + ] + }, + 'service': 'service_undefined', + 'test-metrics': 10 + }; + const serializeMetricsSpy = jest.spyOn(metrics, 'serializeMetrics').mockImplementation(() => mockData); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(serializeMetricsSpy).toBeCalledTimes(1); + expect(consoleLogSpy).toBeCalledTimes(1); + expect(consoleLogSpy).toBeCalledWith(JSON.stringify(mockData)); + + }); }); }); From 01596d364d45d0b1746cf69889391d48dce8b03d Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 15:02:53 +0600 Subject: [PATCH 44/95] test: serializeMetrics should call clearMetrics function --- packages/metrics/tests/unit/Metrics.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 6a7a8489d7..66a32e35ec 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1173,6 +1173,21 @@ describe('Class: Metrics', () => { expect(consoleLogSpy).toBeCalledWith(JSON.stringify(mockData)); }); + + test('it should call clearMetrics function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearMetricsSpy).toBeCalledTimes(1); + + }); }); }); From 8b2abee3ad2e5767e45c2ba328da5d8636eaaacb Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 15:07:40 +0600 Subject: [PATCH 45/95] test: serializeMetrics should call clearDimensions function --- packages/metrics/tests/unit/Metrics.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 66a32e35ec..d658928c48 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1188,6 +1188,22 @@ describe('Class: Metrics', () => { expect(clearMetricsSpy).toBeCalledTimes(1); }); + + test('it should call clearDimensions function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearDimensionsSpy).toBeCalledTimes(1); + + }); + }); }); From 08af29a7966a650206c440780c1d6d71d56240dc Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 15:12:46 +0600 Subject: [PATCH 46/95] test: serializeMetrics should call clearMetadata function --- packages/metrics/tests/unit/Metrics.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d658928c48..cfca6753b7 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1204,6 +1204,21 @@ describe('Class: Metrics', () => { }); + test('it should call clearMetadata function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearMetadataSpy).toBeCalledTimes(1); + + }); + }); }); From 66012a53e41bf5b953758f8c133e16fe42416874 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 22:12:11 +0600 Subject: [PATCH 47/95] test: serializeMetrics should call addMetric with correct parameters --- packages/metrics/tests/unit/Metrics.test.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index cfca6753b7..05ce062d6c 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -24,6 +24,7 @@ describe('Class: Metrics', () => { beforeEach(() => { jest.clearAllMocks(); + jest.resetModules(); }); beforeAll(() => { @@ -1221,4 +1222,25 @@ describe('Class: Metrics', () => { }); + describe('Methods: captureColdStartMetric', () => { + + test('it should call addMetric with correct parameters', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addMetricSpy).toBeCalledTimes(1); + expect(addMetricSpy).toBeCalledWith('ColdStart', MetricUnits.Count, 1); + + }); + + }); }); From d4f7265dd36e152fe07f527491bb35c61ae14184 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 22:39:15 +0600 Subject: [PATCH 48/95] test: serializeMetrics should call setDefaultDimensions with correct parameters --- packages/metrics/tests/unit/Metrics.test.ts | 45 ++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 05ce062d6c..5153af6ab8 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -11,7 +11,7 @@ import { } from '@aws-lambda-powertools/commons'; import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; -import { EmfOutput } from '../../src/types'; +import { Dimensions, EmfOutput } from '../../src/types'; const DEFAULT_NAMESPACE = 'default_namespace'; const mockDate = new Date(1466424490000); @@ -1241,6 +1241,49 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toBeCalledWith('ColdStart', MetricUnits.Count, 1); }); + + test('it should call setDefaultDimensions with correct parameters', () => { + + // Prepare + const defaultDimensions: Dimensions = { + 'foo': 'bar', + 'service': 'order' + }; + const metrics: Metrics = createMetrics({ + namespace: 'test', + defaultDimensions + }); + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledWith({ service: defaultDimensions.service }); + + }); + + test('it should call setDefaultDimensions with correct parameters if not set', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledWith({ service: 'service_undefined' }); + + }); }); }); From 638ccf48e99c79ff04b72d0743a00c30702e2299 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 22:45:05 +0600 Subject: [PATCH 49/95] test: serializeMetrics should call addDimension depending on functionName value --- packages/metrics/tests/unit/Metrics.test.ts | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 5153af6ab8..0913e937f9 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1284,6 +1284,43 @@ describe('Class: Metrics', () => { expect(setDefaultDimensionsSpy).toBeCalledWith({ service: 'service_undefined' }); }); + + test('it should call addDimension, if functionName is set', () => { + + // Prepare + const functionName = 'cold-start'; + const metrics: Metrics = createMetrics({ namespace: 'test' }); + metrics.setFunctionName(functionName); + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledWith('function_name', functionName); + + }); + + test('it should not call addDimension, if functionName is not set', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledTimes(0); + + }); }); }); From 28f9388f91472fa3453fa4591f384b7037ff2211 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 9 Apr 2023 22:53:38 +0600 Subject: [PATCH 50/95] test: serializeMetrics should not call any function, if there is no cold start --- packages/metrics/tests/unit/Metrics.test.ts | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 0913e937f9..7fa1b6d1ce 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1321,6 +1321,29 @@ describe('Class: Metrics', () => { expect(addDimensionSpy).toBeCalledTimes(0); }); + + test('it should not call any function, if there is no cold start', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: 'test' }); + jest.spyOn(metrics, 'isColdStart').mockImplementation(() => false); + + const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(0); + expect(setDefaultDimensionsSpy).toBeCalledTimes(0); + expect(addDimensionSpy).toBeCalledTimes(0); + expect(addMetricSpy).toBeCalledTimes(0); + + }); }); }); From 8d91555ad116dab3057f6e144cd4ee796deaa31a Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 20:54:16 +0600 Subject: [PATCH 51/95] refactor: extract reusable constants to a separate file --- packages/metrics/src/Metrics.ts | 5 +---- packages/metrics/src/constants.ts | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 packages/metrics/src/constants.ts diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index f881bf8dd7..d159102b51 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -2,6 +2,7 @@ import { Callback, Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import { MetricsInterface } from '.'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; +import { MAX_DIMENSION_COUNT,MAX_METRICS_SIZE,DEFAULT_NAMESPACE } from './constants'; import { MetricsOptions, Dimensions, @@ -16,10 +17,6 @@ import { StoredMetric, } from './types'; -const MAX_METRICS_SIZE = 100; -const MAX_DIMENSION_COUNT = 29; -const DEFAULT_NAMESPACE = 'default_namespace'; - /** * ## Intro * Metrics creates custom metrics asynchronously by logging metrics to standard output following Amazon CloudWatch Embedded Metric Format (EMF). diff --git a/packages/metrics/src/constants.ts b/packages/metrics/src/constants.ts new file mode 100644 index 0000000000..bdca3fca96 --- /dev/null +++ b/packages/metrics/src/constants.ts @@ -0,0 +1,3 @@ +export const MAX_METRICS_SIZE = 100; +export const MAX_DIMENSION_COUNT = 29; +export const DEFAULT_NAMESPACE = 'default_namespace'; \ No newline at end of file From 80cf6d33b191beff6a9724ee102e278187c90067 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:02:23 +0600 Subject: [PATCH 52/95] refactor: import default namespace from constants --- packages/metrics/tests/unit/Metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 7fa1b6d1ce..c2560889ce 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -12,8 +12,8 @@ import { import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; +import { DEFAULT_NAMESPACE } from '../../src/constants'; -const DEFAULT_NAMESPACE = 'default_namespace'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); From 67c9cb84299984aa21f4e6eb757f6571db5875ee Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:05:29 +0600 Subject: [PATCH 53/95] refactor: use MAX_METRICS_SIZE from constants --- packages/metrics/tests/unit/Metrics.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index c2560889ce..c45db281ed 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -12,7 +12,7 @@ import { import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; -import { DEFAULT_NAMESPACE } from '../../src/constants'; +import { DEFAULT_NAMESPACE, MAX_METRICS_SIZE } from '../../src/constants'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -166,7 +166,7 @@ describe('Class: Metrics', () => { const metricName = 'test-metric'; //Act - for (let i = 0; i <= 100; i++) { + for (let i = 0; i <= MAX_METRICS_SIZE; i++) { metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); } @@ -183,7 +183,7 @@ describe('Class: Metrics', () => { const metricName = 'test-metric'; //Act - for (let i = 0; i < 100; i++) { + for (let i = 0; i < MAX_METRICS_SIZE; i++) { metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); } From 327011a4b513c60e89c4024c4afa9075936fd4a6 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:18:19 +0600 Subject: [PATCH 54/95] refactor: use MAX_DIMENSION_COUNT from constants --- packages/metrics/tests/unit/Metrics.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index c45db281ed..ede00b8e27 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -12,7 +12,7 @@ import { import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; -import { DEFAULT_NAMESPACE, MAX_METRICS_SIZE } from '../../src/constants'; +import { DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -244,7 +244,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { - for (let i = 0; i < 29; i++) { + for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); } }).toThrowError(RangeError); @@ -320,7 +320,7 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; - for (let i = 0; i <= 29; i++) { + for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } @@ -338,7 +338,7 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; - for (let i = 0; i < 29; i++) { + for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } @@ -440,7 +440,7 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: { [key: string]: string } = {}; - for (let i = 0; i <= 29; i++) { + for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } From f4553cc66ff4ff188ce2b0abe0b71743938ddda8 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:23:22 +0600 Subject: [PATCH 55/95] refactor: use MAX_DIMENSION_COUNT in max dimension allowed tests --- packages/metrics/tests/unit/Metrics.test.ts | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index ede00b8e27..8f1bf13720 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -253,14 +253,15 @@ describe('Class: Metrics', () => { test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { - //Prepare - const metrics = new Metrics({ defaultDimensions: { 'environment': 'prod', 'foo': 'bar' } }); + // Prepare + const defaultDimensions : { [key: string]: string } = { 'environment': 'dev', 'foo': 'bar' }; + const metrics: Metrics = createMetrics({ namespace:'test', defaultDimensions }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; // Act & Assess expect(() => { - for (let i = 0; i < 27; i++) { + for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(defaultDimensions).length); i++) { metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); } }).toThrowError(RangeError); @@ -453,17 +454,16 @@ describe('Class: Metrics', () => { test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { - //Prepare - const metrics = new Metrics({ - defaultDimensions: { - 'test-dimension': 'test-value', - 'environment': 'dev' - } - }); + // Prepare + const initialDefaultDimensions: { [key: string]: string } = { + 'test-dimension': 'test-value', + 'environment': 'dev' + }; + const metrics: Metrics = createMetrics({ namespace: 'test', defaultDimensions: initialDefaultDimensions }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: { [key: string]: string } = {}; - for (let i = 0; i < 27; i++) { + for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(initialDefaultDimensions).length); i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } From aea71c8c5ea9af98b3477594a567836f7063d68c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:28:35 +0600 Subject: [PATCH 56/95] refactor: extract cold start metric in constants --- packages/metrics/src/Metrics.ts | 4 ++-- packages/metrics/src/constants.ts | 5 +++-- packages/metrics/tests/unit/Metrics.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index d159102b51..9485a7f918 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -2,7 +2,7 @@ import { Callback, Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import { MetricsInterface } from '.'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; -import { MAX_DIMENSION_COUNT,MAX_METRICS_SIZE,DEFAULT_NAMESPACE } from './constants'; +import { MAX_DIMENSION_COUNT,MAX_METRICS_SIZE,DEFAULT_NAMESPACE, COLD_START_METRIC } from './constants'; import { MetricsOptions, Dimensions, @@ -225,7 +225,7 @@ class Metrics extends Utility implements MetricsInterface { if (this.functionName != null) { singleMetric.addDimension('function_name', this.functionName); } - singleMetric.addMetric('ColdStart', MetricUnits.Count, 1); + singleMetric.addMetric(COLD_START_METRIC, MetricUnits.Count, 1); } public clearDefaultDimensions(): void { diff --git a/packages/metrics/src/constants.ts b/packages/metrics/src/constants.ts index bdca3fca96..f8cf76c567 100644 --- a/packages/metrics/src/constants.ts +++ b/packages/metrics/src/constants.ts @@ -1,3 +1,4 @@ +export const COLD_START_METRIC = 'ColdStart'; +export const DEFAULT_NAMESPACE = 'default_namespace'; export const MAX_METRICS_SIZE = 100; -export const MAX_DIMENSION_COUNT = 29; -export const DEFAULT_NAMESPACE = 'default_namespace'; \ No newline at end of file +export const MAX_DIMENSION_COUNT = 29; \ No newline at end of file diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 8f1bf13720..d5279106ea 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -12,7 +12,7 @@ import { import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; import { Context } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; -import { DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; +import { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -1238,7 +1238,7 @@ describe('Class: Metrics', () => { // Assess expect(singleMetricSpy).toBeCalledTimes(1); expect(addMetricSpy).toBeCalledTimes(1); - expect(addMetricSpy).toBeCalledWith('ColdStart', MetricUnits.Count, 1); + expect(addMetricSpy).toBeCalledWith(COLD_START_METRIC, MetricUnits.Count, 1); }); From 46f60d0e3c359c2ded70ef0d11ca3354d2d8080e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:49:30 +0600 Subject: [PATCH 57/95] refactor: use createMetrics helper function to create Metrics instance --- packages/metrics/tests/unit/Metrics.test.ts | 74 +++++++++++---------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d5279106ea..f2d148291e 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -37,7 +37,7 @@ describe('Class: Metrics', () => { test('when called, it should store metrics', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const metricName = 'test_metric'; //Act @@ -59,7 +59,7 @@ describe('Class: Metrics', () => { test('when called with multiple metric name, it should store multiple metrics', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addMetric('test_metric-1', MetricUnits.Count, 1, MetricResolution.High); @@ -94,7 +94,7 @@ describe('Class: Metrics', () => { test('when called without resolution, it should store metrics with standard resolution', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addMetric('test-metric-1', MetricUnits.Count, 1); @@ -122,7 +122,7 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times, values should be grouped together in an array', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const metricName = 'test-metric'; //Act @@ -147,7 +147,7 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times but with different unit, it will throw an error', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const metricName = 'test-metric'; // Act & Assess @@ -161,7 +161,7 @@ describe('Class: Metrics', () => { test('it will publish metrics if stored metrics count has reached max metric size threshold', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -178,7 +178,7 @@ describe('Class: Metrics', () => { test('it will not publish metrics if stored metrics count has not reached max metric size threshold', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -198,7 +198,7 @@ describe('Class: Metrics', () => { test('when called, it should clear stored metrics', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const metricName = 'test-metric'; //Act @@ -219,7 +219,7 @@ describe('Class: Metrics', () => { test('when called, it should store dimensions', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const dimensionName = 'test-dimension'; const dimensionValue= 'test-value'; @@ -238,7 +238,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -279,7 +279,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addDimensions(dimensionsToBeAdded); @@ -298,7 +298,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addDimensions(dimensionsToBeAdded); @@ -317,7 +317,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; @@ -335,7 +335,7 @@ describe('Class: Metrics', () => { test('it should successfully add up to maximum allowed dimensions without throwing error', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; @@ -362,7 +362,7 @@ describe('Class: Metrics', () => { 'environment': 'prod', 'foo': 'bar', }; - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); @@ -382,7 +382,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', }; const serviceName = 'test-service'; - const metrics = new Metrics({ serviceName: serviceName }); + const metrics: Metrics = createMetrics({ serviceName: serviceName }); //Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); @@ -402,7 +402,10 @@ describe('Class: Metrics', () => { 'foo': 'bar', }; const serviceName = 'test-service'; - const metrics = new Metrics({ serviceName: serviceName , defaultDimensions: { 'test-dimension': 'test-dimension-value' } }); + const metrics: Metrics = createMetrics({ + serviceName, + defaultDimensions: { 'test-dimension': 'test-dimension-value' } + }); //Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); @@ -422,7 +425,10 @@ describe('Class: Metrics', () => { 'foo': 'bar', }; const serviceName = 'test-service'; - const metrics = new Metrics({ serviceName: serviceName, defaultDimensions: { 'environment': 'dev' } }); + const metrics: Metrics = createMetrics({ + serviceName, + defaultDimensions: { 'environment': 'dev' } + }); //Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); @@ -437,7 +443,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions reaches the maximum allowed', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: { [key: string]: string } = {}; @@ -481,7 +487,7 @@ describe('Class: Metrics', () => { test('it should clear all default dimensions', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); metrics.setDefaultDimensions({ 'foo': 'bar' }); //Act @@ -500,7 +506,7 @@ describe('Class: Metrics', () => { test('it should add metadata', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addMetadata('foo', 'bar'); @@ -515,7 +521,7 @@ describe('Class: Metrics', () => { test('it should update metadata value if added again', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.addMetadata('foo', 'bar'); @@ -534,7 +540,7 @@ describe('Class: Metrics', () => { test('it should clear all dimensions', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); metrics.addDimension('foo', 'bar'); //Act @@ -550,7 +556,7 @@ describe('Class: Metrics', () => { test('it should not clear default dimensions', () => { //Prepare - const metrics = new Metrics({ defaultDimensions: { 'environment': 'prod' } }); + const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'prod' } }); metrics.addDimension('foo', 'bar'); //Act @@ -574,7 +580,7 @@ describe('Class: Metrics', () => { test('it should clear all metadata', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); metrics.addMetadata('foo', 'bar'); metrics.addMetadata('test', 'baz'); @@ -600,7 +606,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics = new Metrics({ + const metrics: Metrics = createMetrics({ namespace, defaultDimensions, singleMetric: false @@ -625,7 +631,7 @@ describe('Class: Metrics', () => { test('it should set the throwOnEmptyMetrics flag to true', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.throwOnEmptyMetrics(); @@ -644,7 +650,7 @@ describe('Class: Metrics', () => { test('it should set the function name', () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); //Act metrics.setFunctionName('test-function'); @@ -666,7 +672,7 @@ describe('Class: Metrics', () => { test('it should log metrics', async () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); @@ -697,7 +703,7 @@ describe('Class: Metrics', () => { test('it should capture cold start metrics, if passed in the options as true', async () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); @@ -728,7 +734,7 @@ describe('Class: Metrics', () => { test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ throwOnEmptyMetrics: true }) @@ -752,7 +758,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); @@ -783,7 +789,7 @@ describe('Class: Metrics', () => { test('it should throw error if lambda handler throws any error', async () => { //Prepare - const metrics = new Metrics(); + const metrics: Metrics = createMetrics(); const errorMessage = 'Unexpected error occurred!'; class LambdaFunction implements LambdaInterface { @@ -811,7 +817,7 @@ describe('Class: Metrics', () => { //Prepare process.env.POWERTOOLS_METRICS_NAMESPACE = ''; - const metrics = new Metrics(); + const metrics: Metrics = new Metrics(); const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); //Act From 151f1192177593777605faf0a5d9c896dce6f768 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 21:58:04 +0600 Subject: [PATCH 58/95] refactor: set namespace for all the Metrics instance --- packages/metrics/tests/unit/Metrics.test.ts | 128 ++++++++++---------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index f2d148291e..8c9e036119 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -19,6 +19,7 @@ const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; + const TEST_NAMESPACE = 'test'; const context = dummyContext.helloworldContext; const event = dummyEvent.Custom.CustomEvent; @@ -37,7 +38,7 @@ describe('Class: Metrics', () => { test('when called, it should store metrics', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test_metric'; //Act @@ -59,7 +60,7 @@ describe('Class: Metrics', () => { test('when called with multiple metric name, it should store multiple metrics', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test_metric-1', MetricUnits.Count, 1, MetricResolution.High); @@ -94,7 +95,7 @@ describe('Class: Metrics', () => { test('when called without resolution, it should store metrics with standard resolution', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metric-1', MetricUnits.Count, 1); @@ -122,7 +123,7 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times, values should be grouped together in an array', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; //Act @@ -147,7 +148,7 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times but with different unit, it will throw an error', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; // Act & Assess @@ -161,7 +162,7 @@ describe('Class: Metrics', () => { test('it will publish metrics if stored metrics count has reached max metric size threshold', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -178,7 +179,7 @@ describe('Class: Metrics', () => { test('it will not publish metrics if stored metrics count has not reached max metric size threshold', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -198,7 +199,7 @@ describe('Class: Metrics', () => { test('when called, it should clear stored metrics', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; //Act @@ -219,7 +220,7 @@ describe('Class: Metrics', () => { test('when called, it should store dimensions', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue= 'test-value'; @@ -238,7 +239,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -279,7 +280,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addDimensions(dimensionsToBeAdded); @@ -298,7 +299,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addDimensions(dimensionsToBeAdded); @@ -317,7 +318,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; @@ -335,7 +336,7 @@ describe('Class: Metrics', () => { test('it should successfully add up to maximum allowed dimensions without throwing error', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: { [key: string]: string } = {}; @@ -362,7 +363,7 @@ describe('Class: Metrics', () => { 'environment': 'prod', 'foo': 'bar', }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); @@ -403,6 +404,7 @@ describe('Class: Metrics', () => { }; const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, serviceName, defaultDimensions: { 'test-dimension': 'test-dimension-value' } }); @@ -426,6 +428,7 @@ describe('Class: Metrics', () => { }; const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, serviceName, defaultDimensions: { 'environment': 'dev' } }); @@ -443,7 +446,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions reaches the maximum allowed', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: { [key: string]: string } = {}; @@ -465,7 +468,10 @@ describe('Class: Metrics', () => { 'test-dimension': 'test-value', 'environment': 'dev' }; - const metrics: Metrics = createMetrics({ namespace: 'test', defaultDimensions: initialDefaultDimensions }); + const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, + defaultDimensions: initialDefaultDimensions + }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: { [key: string]: string } = {}; @@ -487,7 +493,7 @@ describe('Class: Metrics', () => { test('it should clear all default dimensions', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.setDefaultDimensions({ 'foo': 'bar' }); //Act @@ -506,7 +512,7 @@ describe('Class: Metrics', () => { test('it should add metadata', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetadata('foo', 'bar'); @@ -521,7 +527,7 @@ describe('Class: Metrics', () => { test('it should update metadata value if added again', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetadata('foo', 'bar'); @@ -540,7 +546,7 @@ describe('Class: Metrics', () => { test('it should clear all dimensions', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addDimension('foo', 'bar'); //Act @@ -580,7 +586,7 @@ describe('Class: Metrics', () => { test('it should clear all metadata', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetadata('foo', 'bar'); metrics.addMetadata('test', 'baz'); @@ -601,13 +607,12 @@ describe('Class: Metrics', () => { test('it should return a single Metric object', () => { //Prepare - const namespace = 'test-namespace'; const defaultDimensions = { 'foo': 'bar', 'service': 'order' }; const metrics: Metrics = createMetrics({ - namespace, + namespace: TEST_NAMESPACE, defaultDimensions, singleMetric: false }); @@ -618,7 +623,7 @@ describe('Class: Metrics', () => { //Asses expect(singleMetric).toEqual(expect.objectContaining({ isSingleMetric: true, - namespace, + namespace: TEST_NAMESPACE, defaultDimensions })); @@ -631,7 +636,7 @@ describe('Class: Metrics', () => { test('it should set the throwOnEmptyMetrics flag to true', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.throwOnEmptyMetrics(); @@ -650,7 +655,7 @@ describe('Class: Metrics', () => { test('it should set the function name', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.setFunctionName('test-function'); @@ -672,7 +677,7 @@ describe('Class: Metrics', () => { test('it should log metrics', async () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); @@ -703,7 +708,7 @@ describe('Class: Metrics', () => { test('it should capture cold start metrics, if passed in the options as true', async () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); @@ -734,7 +739,7 @@ describe('Class: Metrics', () => { test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ throwOnEmptyMetrics: true }) @@ -758,7 +763,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); @@ -789,7 +794,7 @@ describe('Class: Metrics', () => { test('it should throw error if lambda handler throws any error', async () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const errorMessage = 'Unexpected error occurred!'; class LambdaFunction implements LambdaInterface { @@ -901,7 +906,7 @@ describe('Class: Metrics', () => { //Prepare const serviceName = 'hello-world-service'; process.env.POWERTOOLS_SERVICE_NAME = serviceName; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); @@ -938,7 +943,7 @@ describe('Class: Metrics', () => { //Prepare const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); @@ -959,7 +964,7 @@ describe('Class: Metrics', () => { metric2: 'metric2Value', metric3: 'metric3Value' }; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); @@ -978,7 +983,7 @@ describe('Class: Metrics', () => { test('it should log metadata correctly', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); @@ -993,7 +998,7 @@ describe('Class: Metrics', () => { test('it should throw error on empty metrics when throwOnEmptyMetrics is true', () => { //Prepare - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.throwOnEmptyMetrics(); @@ -1020,15 +1025,14 @@ describe('Class: Metrics', () => { test('it should use namespace provided in constructor', () => { //Prepare - const namespace = 'test-namespace'; - const metrics: Metrics = createMetrics({ namespace: namespace }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); //Assess - expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(namespace); + expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(TEST_NAMESPACE); }); @@ -1036,7 +1040,7 @@ describe('Class: Metrics', () => { //Prepare const metricName = 'test-metrics'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1052,7 +1056,7 @@ describe('Class: Metrics', () => { //Prepare const metricName = 'test-metrics'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1070,7 +1074,7 @@ describe('Class: Metrics', () => { //Prepare const metricName1 = 'test-metrics'; const metricName2 = 'test-metrics-2'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric(metricName1, MetricUnits.Count, 10); @@ -1088,7 +1092,7 @@ describe('Class: Metrics', () => { //Prepare const metricName = 'test-metrics'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1105,7 +1109,7 @@ describe('Class: Metrics', () => { //Prepare const metricName = 'test-metrics'; const metricName2 = 'test-metrics-2'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); //Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1126,7 +1130,7 @@ describe('Class: Metrics', () => { test('it should console warning if no metrics are added', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); // Act @@ -1143,7 +1147,7 @@ describe('Class: Metrics', () => { test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metrics', MetricUnits.Count, 10); const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); const mockData: EmfOutput = { @@ -1184,7 +1188,7 @@ describe('Class: Metrics', () => { test('it should call clearMetrics function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metrics', MetricUnits.Count, 10); const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); @@ -1199,7 +1203,7 @@ describe('Class: Metrics', () => { test('it should call clearDimensions function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metrics', MetricUnits.Count, 10); const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); @@ -1214,7 +1218,7 @@ describe('Class: Metrics', () => { test('it should call clearMetadata function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metrics', MetricUnits.Count, 10); const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); @@ -1233,8 +1237,8 @@ describe('Class: Metrics', () => { test('it should call addMetric with correct parameters', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); @@ -1256,10 +1260,10 @@ describe('Class: Metrics', () => { 'service': 'order' }; const metrics: Metrics = createMetrics({ - namespace: 'test', + namespace: TEST_NAMESPACE, defaultDimensions }); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -1276,8 +1280,8 @@ describe('Class: Metrics', () => { test('it should call setDefaultDimensions with correct parameters if not set', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -1295,9 +1299,9 @@ describe('Class: Metrics', () => { // Prepare const functionName = 'cold-start'; - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.setFunctionName(functionName); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); @@ -1314,8 +1318,8 @@ describe('Class: Metrics', () => { test('it should not call addDimension, if functionName is not set', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); @@ -1331,10 +1335,10 @@ describe('Class: Metrics', () => { test('it should not call any function, if there is no cold start', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: 'test' }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); jest.spyOn(metrics, 'isColdStart').mockImplementation(() => false); - const singleMetricMock: Metrics = createMetrics({ namespace: 'test', singleMetric: true }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); From dd1f790087921b3f1dcc12bc14055068992b1f2c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 22:01:10 +0600 Subject: [PATCH 59/95] style: formatting in test steps --- packages/metrics/tests/unit/Metrics.test.ts | 212 ++++++++++---------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 8c9e036119..ec27d78433 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -37,11 +37,11 @@ describe('Class: Metrics', () => { test('when called, it should store metrics', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test_metric'; - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 1, MetricResolution.High); // Assess @@ -59,10 +59,10 @@ describe('Class: Metrics', () => { test('when called with multiple metric name, it should store multiple metrics', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test_metric-1', MetricUnits.Count, 1, MetricResolution.High); metrics.addMetric('test_metric-2', MetricUnits.Count, 3, MetricResolution.High); metrics.addMetric('test_metric-3', MetricUnits.Count, 6, MetricResolution.High); @@ -94,10 +94,10 @@ describe('Class: Metrics', () => { test('when called without resolution, it should store metrics with standard resolution', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metric-1', MetricUnits.Count, 1); metrics.addMetric('test-metric-2', MetricUnits.Seconds, 3); @@ -122,11 +122,11 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times, values should be grouped together in an array', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 1); metrics.addMetric(metricName, MetricUnits.Count, 5); metrics.addMetric(metricName, MetricUnits.Count, 1); @@ -147,7 +147,7 @@ describe('Class: Metrics', () => { test('when trying to add metric with the same name multiple times but with different unit, it will throw an error', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; @@ -161,12 +161,12 @@ describe('Class: Metrics', () => { test('it will publish metrics if stored metrics count has reached max metric size threshold', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; - //Act + // Act for (let i = 0; i <= MAX_METRICS_SIZE; i++) { metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); } @@ -178,12 +178,12 @@ describe('Class: Metrics', () => { test('it will not publish metrics if stored metrics count has not reached max metric size threshold', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; - //Act + // Act for (let i = 0; i < MAX_METRICS_SIZE; i++) { metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); } @@ -198,11 +198,11 @@ describe('Class: Metrics', () => { test('when called, it should clear stored metrics', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 1); metrics.clearMetrics(); @@ -219,12 +219,12 @@ describe('Class: Metrics', () => { test('when called, it should store dimensions', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue= 'test-value'; - //Act + // Act metrics.addDimension(dimensionName, dimensionValue); // Assess @@ -238,7 +238,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -275,14 +275,14 @@ describe('Class: Metrics', () => { test('it should add multiple dimensions', () => { - //Prepare + // Prepare const dimensionsToBeAdded: { [key: string]: string } = { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addDimensions(dimensionsToBeAdded); // Assess @@ -294,14 +294,14 @@ describe('Class: Metrics', () => { test('if same dimension is added again, it should update existing dimension value', () => { - //Prepare + // Prepare const dimensionsToBeAdded: { [key: string]: string } = { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addDimensions(dimensionsToBeAdded); metrics.addDimensions({ 'test-dimension-1': 'test-value-3' }); @@ -317,7 +317,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -335,7 +335,7 @@ describe('Class: Metrics', () => { test('it should successfully add up to maximum allowed dimensions without throwing error', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -358,14 +358,14 @@ describe('Class: Metrics', () => { test('it should set default dimensions when service name is not provided', () => { - //Prepare + // Prepare const defaultDimensionsToBeAdded = { 'environment': 'prod', 'foo': 'bar', }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess @@ -377,7 +377,7 @@ describe('Class: Metrics', () => { test('it should set default dimensions when service name is provided', () => { - //Prepare + // Prepare const defaultDimensionsToBeAdded = { 'environment': 'prod', 'foo': 'bar', @@ -385,7 +385,7 @@ describe('Class: Metrics', () => { const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ serviceName: serviceName }); - //Act + // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess @@ -397,7 +397,7 @@ describe('Class: Metrics', () => { test('it should add default dimensions', () => { - //Prepare + // Prepare const defaultDimensionsToBeAdded = { 'environment': 'prod', 'foo': 'bar', @@ -409,7 +409,7 @@ describe('Class: Metrics', () => { defaultDimensions: { 'test-dimension': 'test-dimension-value' } }); - //Act + // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess @@ -421,7 +421,7 @@ describe('Class: Metrics', () => { test('it should update already added default dimensions values', () => { - //Prepare + // Prepare const defaultDimensionsToBeAdded = { 'environment': 'prod', 'foo': 'bar', @@ -433,7 +433,7 @@ describe('Class: Metrics', () => { defaultDimensions: { 'environment': 'dev' } }); - //Act + // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess @@ -445,7 +445,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions reaches the maximum allowed', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -492,11 +492,11 @@ describe('Class: Metrics', () => { test('it should clear all default dimensions', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.setDefaultDimensions({ 'foo': 'bar' }); - //Act + // Act metrics.clearDefaultDimensions(); // Assess @@ -511,10 +511,10 @@ describe('Class: Metrics', () => { test('it should add metadata', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetadata('foo', 'bar'); // Assess @@ -526,10 +526,10 @@ describe('Class: Metrics', () => { test('it should update metadata value if added again', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetadata('foo', 'bar'); metrics.addMetadata('foo', 'baz'); @@ -545,11 +545,11 @@ describe('Class: Metrics', () => { test('it should clear all dimensions', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addDimension('foo', 'bar'); - //Act + // Act metrics.clearDimensions(); // Assess @@ -561,11 +561,11 @@ describe('Class: Metrics', () => { test('it should not clear default dimensions', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'prod' } }); metrics.addDimension('foo', 'bar'); - //Act + // Act metrics.clearDimensions(); // Assess @@ -585,12 +585,12 @@ describe('Class: Metrics', () => { test('it should clear all metadata', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.addMetadata('foo', 'bar'); metrics.addMetadata('test', 'baz'); - //Act + // Act metrics.clearMetadata(); // Assess @@ -606,7 +606,7 @@ describe('Class: Metrics', () => { test('it should return a single Metric object', () => { - //Prepare + // Prepare const defaultDimensions = { 'foo': 'bar', 'service': 'order' @@ -617,7 +617,7 @@ describe('Class: Metrics', () => { singleMetric: false }); - //Act + // Act const singleMetric = metrics.singleMetric(); //Asses @@ -635,13 +635,13 @@ describe('Class: Metrics', () => { test('it should set the throwOnEmptyMetrics flag to true', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.throwOnEmptyMetrics(); - //Assess + // Assess expect(metrics).toEqual(expect.objectContaining({ shouldThrowOnEmptyMetrics: true })); @@ -654,13 +654,13 @@ describe('Class: Metrics', () => { test('it should set the function name', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.setFunctionName('test-function'); - //Assess + // Assess expect(metrics).toEqual(expect.objectContaining({ functionName: 'test-function' })); @@ -676,7 +676,7 @@ describe('Class: Metrics', () => { test('it should log metrics', async () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); @@ -707,7 +707,7 @@ describe('Class: Metrics', () => { test('it should capture cold start metrics, if passed in the options as true', async () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const addMetricSpy = jest.spyOn(metrics, 'addMetric'); @@ -738,7 +738,7 @@ describe('Class: Metrics', () => { test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); class LambdaFunction implements LambdaInterface { @@ -758,7 +758,7 @@ describe('Class: Metrics', () => { test('it should set default dimensions if passed in the options', async () => { - //Prepare + // Prepare const defaultDimensions = { 'foo': 'bar', 'service': 'order' @@ -793,7 +793,7 @@ describe('Class: Metrics', () => { test('it should throw error if lambda handler throws any error', async () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const errorMessage = 'Unexpected error occurred!'; class LambdaFunction implements LambdaInterface { @@ -820,22 +820,22 @@ describe('Class: Metrics', () => { test('it should print warning, if no namespace provided in constructor or environment variable', () => { - //Prepare + // Prepare process.env.POWERTOOLS_METRICS_NAMESPACE = ''; const metrics: Metrics = new Metrics(); const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); - //Act + // Act metrics.serializeMetrics(); - //Assess + // Assess expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); }); test('it should return right object compliant with Cloudwatch EMF', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: 'test-namespace', serviceName: 'test-service', @@ -844,13 +844,13 @@ describe('Class: Metrics', () => { } }); - //Act + // Act metrics.addMetric('successfulBooking', MetricUnits.Count, 1); metrics.addMetric('successfulBooking', MetricUnits.Count, 3); metrics.addMetric('failedBooking', MetricUnits.Count, 1, MetricResolution.High); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData).toEqual( { '_aws': { @@ -888,31 +888,31 @@ describe('Class: Metrics', () => { test('it should log service dimension correctly when passed', () => { - //Prepare + // Prepare const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ serviceName:serviceName }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData.service).toEqual(serviceName); }); test('it should log service dimension correctly from env var when not passed', () => { - //Prepare + // Prepare const serviceName = 'hello-world-service'; process.env.POWERTOOLS_SERVICE_NAME = serviceName; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData.service).toEqual(serviceName); delete process.env.POWERTOOLS_SERVICE_NAME; @@ -920,18 +920,18 @@ describe('Class: Metrics', () => { test('it should log default dimensions correctly', () => { - //Prepare + // Prepare const additionalDimensions = { 'foo': 'bar', 'env': 'dev' }; const metrics: Metrics = createMetrics({ defaultDimensions: additionalDimensions }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); expect(loggedData.service).toEqual(defaultServiceName); expect(loggedData.foo).toEqual(additionalDimensions.foo); @@ -941,16 +941,16 @@ describe('Class: Metrics', () => { test('it should log additional dimensions correctly', () => { - //Prepare + // Prepare const additionalDimension = { name: 'metric2', value: 'metric2Value' }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); metrics.addDimension(additionalDimension.name, additionalDimension.value); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); expect(loggedData.service).toEqual(defaultServiceName); expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); @@ -959,19 +959,19 @@ describe('Class: Metrics', () => { test('it should log additional bulk dimensions correctly', () => { - //Prepare + // Prepare const additionalDimensions: { [key: string]: string } = { metric2: 'metric2Value', metric3: 'metric3Value' }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); metrics.addDimensions(additionalDimensions); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(3); expect(loggedData.service).toEqual(defaultServiceName); Object.keys(additionalDimensions).forEach((key) => { @@ -982,71 +982,71 @@ describe('Class: Metrics', () => { test('it should log metadata correctly', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); metrics.addMetadata('foo', 'bar'); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData.foo).toEqual('bar'); }); test('it should throw error on empty metrics when throwOnEmptyMetrics is true', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.throwOnEmptyMetrics(); - //Assess + // Assess expect(() => metrics.serializeMetrics()).toThrow('The number of metrics recorded must be higher than zero'); }); test('it should use default namespace when not provided', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics(); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(DEFAULT_NAMESPACE); }); test('it should use namespace provided in constructor', () => { - //Prepare + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric('test-metrics', MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(TEST_NAMESPACE); }); test('it should contain a metric value if added once', () => { - //Prepare + // Prepare const metricName = 'test-metrics'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); expect(loggedData['test-metrics']).toEqual(10); @@ -1054,16 +1054,16 @@ describe('Class: Metrics', () => { test('it should convert metric value with the same name and unit to array if added multiple times', () => { - //Prepare + // Prepare const metricName = 'test-metrics'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 10); metrics.addMetric(metricName, MetricUnits.Count, 20); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); expect(loggedData[metricName]).toEqual([ 10, 20 ]); @@ -1071,17 +1071,17 @@ describe('Class: Metrics', () => { test('it should create multiple metric values if added multiple times', () => { - //Prepare + // Prepare const metricName1 = 'test-metrics'; const metricName2 = 'test-metrics-2'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric(metricName1, MetricUnits.Count, 10); metrics.addMetric(metricName2, MetricUnits.Seconds, 20); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); expect(loggedData[metricName1]).toEqual(10); expect(loggedData[metricName2]).toEqual(20); @@ -1090,15 +1090,15 @@ describe('Class: Metrics', () => { test('it should not contain `StorageResolution` as key for non-high resolution metrics', () => { - //Prepare + // Prepare const metricName = 'test-metrics'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); @@ -1106,17 +1106,17 @@ describe('Class: Metrics', () => { test('it should contain `StorageResolution` as key & high metric resolution as value for high resolution metrics', () => { - //Prepare + // Prepare const metricName = 'test-metrics'; const metricName2 = 'test-metrics-2'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - //Act + // Act metrics.addMetric(metricName, MetricUnits.Count, 10); metrics.addMetric(metricName2, MetricUnits.Seconds, 10, MetricResolution.High); const loggedData = metrics.serializeMetrics(); - //Assess + // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); expect(loggedData._aws.CloudWatchMetrics[0].Metrics[1].StorageResolution).toEqual(MetricResolution.High); From 77923b8ac6b40a494adfbe04481f8ae36d29be3c Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 22:04:50 +0600 Subject: [PATCH 60/95] test: cleanup POWERTOOLS_METRICS_NAMESPACE env after test completion inside serializeMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index ec27d78433..63c93fb247 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -830,6 +830,7 @@ describe('Class: Metrics', () => { // Assess expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); + delete process.env.POWERTOOLS_METRICS_NAMESPACE; }); From eedbc0ffb6d3811246bfb841732f55a8feb4cc33 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 10 Apr 2023 22:16:39 +0600 Subject: [PATCH 61/95] test: error messages for various errors --- packages/metrics/tests/unit/Metrics.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 63c93fb247..a4d697de8c 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -155,7 +155,7 @@ describe('Class: Metrics', () => { expect(() => { metrics.addMetric(metricName, MetricUnits.Count, 1); metrics.addMetric(metricName, MetricUnits.Kilobits, 5); - }).toThrowError(Error); + }).toThrowError(`Metric "${metricName}" has already been added with unit "${MetricUnits.Count}", but we received unit "${MetricUnits.Kilobits}". Did you mean to use metric unit "${MetricUnits.Count}"?`); }); @@ -248,7 +248,7 @@ describe('Class: Metrics', () => { for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); } - }).toThrowError(RangeError); + }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); @@ -265,7 +265,7 @@ describe('Class: Metrics', () => { for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(defaultDimensions).length); i++) { metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); } - }).toThrowError(RangeError); + }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); @@ -329,7 +329,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { metrics.addDimensions(dimensionsToBeAdded); - }).toThrowError(RangeError); + }).toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); @@ -347,7 +347,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { metrics.addDimensions(dimensionsToBeAdded); - }).not.toThrowError(RangeError); + }).not.toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); expect(metrics).toEqual(expect.objectContaining({ dimensions: dimensionsToBeAdded })); }); @@ -457,7 +457,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { metrics.setDefaultDimensions(defaultDimensions); - }).toThrowError(Error); + }).toThrowError('Max dimension count hit'); }); @@ -482,7 +482,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { metrics.setDefaultDimensions(defaultDimensions); - }).toThrowError(Error); + }).toThrowError('Max dimension count hit'); }); @@ -752,7 +752,7 @@ describe('Class: Metrics', () => { const handler = handlerClass.handler.bind(handlerClass); // Act & Assess - await expect(handler(event, context)).rejects.toThrowError(RangeError); + await expect(handler(event, context)).rejects.toThrowError('The number of metrics recorded must be higher than zero'); }); From 12d12a3b6b9e0156bb6331b9132eb3d723925a72 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 13 Apr 2023 22:33:57 +0600 Subject: [PATCH 62/95] docs: decorator function comments --- packages/metrics/tests/unit/Metrics.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index a4d697de8c..9990b0438c 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -3,7 +3,6 @@ * * @group unit/metrics/class */ - import { LambdaInterface, ContextExamples as dummyContext, @@ -684,6 +683,8 @@ describe('Class: Metrics', () => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics() + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { metrics.addMetric(testMetric, MetricUnits.Count, 1); @@ -715,6 +716,8 @@ describe('Class: Metrics', () => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ captureColdStartMetric: true }) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { metrics.addMetric(testMetric, MetricUnits.Count, 1); @@ -743,6 +746,8 @@ describe('Class: Metrics', () => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ throwOnEmptyMetrics: true }) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { return expectedReturnValue; } @@ -771,6 +776,8 @@ describe('Class: Metrics', () => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ defaultDimensions }) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { metrics.addMetric(testMetric, MetricUnits.Count, 1); @@ -799,6 +806,8 @@ describe('Class: Metrics', () => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics() + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { throw new Error(errorMessage); } From f439dc131385dfa0fc70fc4d8ba41a3ea859c3a7 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sat, 15 Apr 2023 11:53:40 +0600 Subject: [PATCH 63/95] refactor: format test metric name --- packages/metrics/tests/unit/Metrics.test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 9990b0438c..1b8751cd5f 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -38,7 +38,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const metricName = 'test_metric'; + const metricName = 'test-metric'; // Act metrics.addMetric(metricName, MetricUnits.Count, 1, MetricResolution.High); @@ -62,27 +62,27 @@ describe('Class: Metrics', () => { const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test_metric-1', MetricUnits.Count, 1, MetricResolution.High); - metrics.addMetric('test_metric-2', MetricUnits.Count, 3, MetricResolution.High); - metrics.addMetric('test_metric-3', MetricUnits.Count, 6, MetricResolution.High); + metrics.addMetric('test-metric-1', MetricUnits.Count, 1, MetricResolution.High); + metrics.addMetric('test-metric-2', MetricUnits.Count, 3, MetricResolution.High); + metrics.addMetric('test-metric-3', MetricUnits.Count, 6, MetricResolution.High); // Assess expect(metrics).toEqual(expect.objectContaining({ storedMetrics: { - 'test_metric-1': { - name: 'test_metric-1', + 'test-metric-1': { + name: 'test-metric-1', resolution: MetricResolution.High, unit: MetricUnits.Count, value: 1 }, - 'test_metric-2': { - name: 'test_metric-2', + 'test-metric-2': { + name: 'test-metric-2', resolution: MetricResolution.High, unit: MetricUnits.Count, value: 3 }, - 'test_metric-3': { - name: 'test_metric-3', + 'test-metric-3': { + name: 'test-metric-3', resolution: MetricResolution.High, unit: MetricUnits.Count, value: 6 From efa136e40a9776aede5ca2c9a3a1ee6eee9ea5ca Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 14:36:00 +0600 Subject: [PATCH 64/95] style: rearrange tests by name --- packages/metrics/tests/unit/Metrics.test.ts | 1224 ++++++++++--------- 1 file changed, 613 insertions(+), 611 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 1b8751cd5f..c7620b9c37 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -17,6 +17,7 @@ const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); describe('Class: Metrics', () => { + const ENVIRONMENT_VARIABLES = process.env; const TEST_NAMESPACE = 'test'; const context = dummyContext.helloworldContext; @@ -32,6 +33,179 @@ describe('Class: Metrics', () => { process.env = { ...ENVIRONMENT_VARIABLES }; }); + describe('Method: addDimension', () => { + + test('when called, it should store dimensions', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const dimensionName = 'test-dimension'; + const dimensionValue= 'test-value'; + + // Act + metrics.addDimension(dimensionName, dimensionValue); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: { + [dimensionName]: dimensionValue + }, + })); + + }); + + test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + + // Act & Assess + expect(() => { + for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + + }); + + test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { + + // Prepare + const defaultDimensions : { [key: string]: string } = { 'environment': 'dev', 'foo': 'bar' }; + const metrics: Metrics = createMetrics({ namespace:'test', defaultDimensions }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + + // Act & Assess + expect(() => { + for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(defaultDimensions).length); i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + + }); + + }); + + describe('Method: addDimensions', () => { + + test('it should add multiple dimensions', () => { + + // Prepare + const dimensionsToBeAdded: { [key: string]: string } = { + 'test-dimension-1': 'test-value-1', + 'test-dimension-2': 'test-value-2', + }; + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + + // Act + metrics.addDimensions(dimensionsToBeAdded); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: dimensionsToBeAdded + })); + + }); + + test('if same dimension is added again, it should update existing dimension value', () => { + + // Prepare + const dimensionsToBeAdded: { [key: string]: string } = { + 'test-dimension-1': 'test-value-1', + 'test-dimension-2': 'test-value-2', + }; + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + + // Act + metrics.addDimensions(dimensionsToBeAdded); + metrics.addDimensions({ 'test-dimension-1': 'test-value-3' }); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: { + 'test-dimension-1': 'test-value-3', + 'test-dimension-2': 'test-value-2', + } + })); + + }); + + test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const dimensionsToBeAdded: { [key: string]: string } = {}; + for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { + dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.addDimensions(dimensionsToBeAdded); + }).toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + + }); + + test('it should successfully add up to maximum allowed dimensions without throwing error', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const dimensionsToBeAdded: { [key: string]: string } = {}; + for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { + dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.addDimensions(dimensionsToBeAdded); + }).not.toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + expect(metrics).toEqual(expect.objectContaining({ dimensions: dimensionsToBeAdded })); + + }); + + }); + + describe('Method: addMetadata', () => { + + test('it should add metadata', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + + // Act + metrics.addMetadata('foo', 'bar'); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + metadata: { 'foo': 'bar' } + })); + + }); + + test('it should update metadata value if added again', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + + // Act + metrics.addMetadata('foo', 'bar'); + metrics.addMetadata('foo', 'baz'); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + metadata: { 'foo': 'baz' } + })); + + }); + }); + describe('Method: addMetric', () => { test('when called, it should store metrics', () => { @@ -193,481 +367,232 @@ describe('Class: Metrics', () => { }); }); - describe('Method: clearMetrics', () => { + describe('Methods: captureColdStartMetric', () => { - test('when called, it should clear stored metrics', () => { - + test('it should call addMetric with correct parameters', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const metricName = 'test-metric'; - - // Act - metrics.addMetric(metricName, MetricUnits.Count, 1); - metrics.clearMetrics(); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); + + // Act + metrics.captureColdStartMetric(); // Assess - expect(metrics).toEqual(expect.objectContaining({ - storedMetrics: {}, - })); - + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addMetricSpy).toBeCalledTimes(1); + expect(addMetricSpy).toBeCalledWith(COLD_START_METRIC, MetricUnits.Count, 1); + }); - - }); - describe('Method: addDimension', () => { - - test('when called, it should store dimensions', () => { - + test('it should call setDefaultDimensions with correct parameters', () => { + // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const dimensionName = 'test-dimension'; - const dimensionValue= 'test-value'; - - // Act - metrics.addDimension(dimensionName, dimensionValue); - + const defaultDimensions: Dimensions = { + 'foo': 'bar', + 'service': 'order' + }; + const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, + defaultDimensions + }); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); + + // Act + metrics.captureColdStartMetric(); + // Assess - expect(metrics).toEqual(expect.objectContaining({ - dimensions: { - [dimensionName]: dimensionValue - }, - })); - + expect(singleMetricSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledWith({ service: defaultDimensions.service }); + }); - test('it should throw error if number of dimensions exceeds the maximum allowed', () => { - + test('it should call setDefaultDimensions with correct parameters if not set', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - - // Act & Assess - expect(() => { - for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - - }); - - test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); - // Prepare - const defaultDimensions : { [key: string]: string } = { 'environment': 'dev', 'foo': 'bar' }; - const metrics: Metrics = createMetrics({ namespace:'test', defaultDimensions }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - - // Act & Assess - expect(() => { - for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(defaultDimensions).length); i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledTimes(1); + expect(setDefaultDimensionsSpy).toBeCalledWith({ service: 'service_undefined' }); + }); - }); - - describe('Method: addDimensions', () => { - - test('it should add multiple dimensions', () => { - + test('it should call addDimension, if functionName is set', () => { + // Prepare - const dimensionsToBeAdded: { [key: string]: string } = { - 'test-dimension-1': 'test-value-1', - 'test-dimension-2': 'test-value-2', - }; + const functionName = 'cold-start'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - - // Act - metrics.addDimensions(dimensionsToBeAdded); - + metrics.setFunctionName(functionName); + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + // Assess - expect(metrics).toEqual(expect.objectContaining({ - dimensions: dimensionsToBeAdded - })); - + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledWith('function_name', functionName); + }); - test('if same dimension is added again, it should update existing dimension value', () => { - + test('it should not call addDimension, if functionName is not set', () => { + // Prepare - const dimensionsToBeAdded: { [key: string]: string } = { - 'test-dimension-1': 'test-value-1', - 'test-dimension-2': 'test-value-2', - }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - - // Act - metrics.addDimensions(dimensionsToBeAdded); - metrics.addDimensions({ 'test-dimension-1': 'test-value-3' }); - + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + // Assess - expect(metrics).toEqual(expect.objectContaining({ - dimensions: { - 'test-dimension-1': 'test-value-3', - 'test-dimension-2': 'test-value-2', - } - })); - + expect(singleMetricSpy).toBeCalledTimes(1); + expect(addDimensionSpy).toBeCalledTimes(0); + }); - test('it should throw error if number of dimensions exceeds the maximum allowed', () => { - + test('it should not call any function, if there is no cold start', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - const dimensionsToBeAdded: { [key: string]: string } = {}; - for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { - dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; - } - - // Act & Assess - expect(() => { - metrics.addDimensions(dimensionsToBeAdded); - }).toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - - }); + jest.spyOn(metrics, 'isColdStart').mockImplementation(() => false); - test('it should successfully add up to maximum allowed dimensions without throwing error', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - const dimensionsToBeAdded: { [key: string]: string } = {}; - for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { - dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; - } - - // Act & Assess - expect(() => { - metrics.addDimensions(dimensionsToBeAdded); - }).not.toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - expect(metrics).toEqual(expect.objectContaining({ dimensions: dimensionsToBeAdded })); - + const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); + const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); + const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); + const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); + + // Act + metrics.captureColdStartMetric(); + + // Assess + expect(singleMetricSpy).toBeCalledTimes(0); + expect(setDefaultDimensionsSpy).toBeCalledTimes(0); + expect(addDimensionSpy).toBeCalledTimes(0); + expect(addMetricSpy).toBeCalledTimes(0); + }); - + }); - describe('Method: setDefaultDimensions', () => { - - test('it should set default dimensions when service name is not provided', () => { + describe('Method: clearDefaultDimensions', () => { + + test('it should clear all default dimensions', () => { // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.setDefaultDimensions({ 'foo': 'bar' }); // Act - metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + metrics.clearDefaultDimensions(); // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : 'service_undefined' } + defaultDimensions: {} })); }); + }); - test('it should set default dimensions when service name is provided', () => { - - // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; - const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ serviceName: serviceName }); + describe('Method: clearDimensions', () => { + test('it should clear all dimensions', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addDimension('foo', 'bar'); + // Act - metrics.setDefaultDimensions(defaultDimensionsToBeAdded); - + metrics.clearDimensions(); + // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName } + dimensions: {} })); - + }); - test('it should add default dimensions', () => { - + test('it should not clear default dimensions', () => { + // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; - const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ - namespace: TEST_NAMESPACE, - serviceName, - defaultDimensions: { 'test-dimension': 'test-dimension-value' } - }); - + const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'prod' } }); + metrics.addDimension('foo', 'bar'); + // Act - metrics.setDefaultDimensions(defaultDimensionsToBeAdded); - + metrics.clearDimensions(); + // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName , 'test-dimension': 'test-dimension-value' } + dimensions: {}, + defaultDimensions: { + 'environment': 'prod', + 'service': 'service_undefined' + } })); - + }); - test('it should update already added default dimensions values', () => { - - // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; - const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ - namespace: TEST_NAMESPACE, - serviceName, - defaultDimensions: { 'environment': 'dev' } - }); + }); + + describe('Method: clearMetadata', () => { + test('it should clear all metadata', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addMetadata('foo', 'bar'); + metrics.addMetadata('test', 'baz'); + // Act - metrics.setDefaultDimensions(defaultDimensionsToBeAdded); - + metrics.clearMetadata(); + // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { foo: 'bar', service: serviceName, 'environment': 'prod' } + metadata: {} })); - + }); - test('it should throw error if number of dimensions reaches the maximum allowed', () => { + }); + + describe('Method: clearMetrics', () => { + + test('when called, it should clear stored metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - const defaultDimensions: { [key: string]: string } = {}; - for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { - defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; - } - - // Act & Assess - expect(() => { - metrics.setDefaultDimensions(defaultDimensions); - }).toThrowError('Max dimension count hit'); - - }); - - test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { - - // Prepare - const initialDefaultDimensions: { [key: string]: string } = { - 'test-dimension': 'test-value', - 'environment': 'dev' - }; - const metrics: Metrics = createMetrics({ - namespace: TEST_NAMESPACE, - defaultDimensions: initialDefaultDimensions - }); - const dimensionName = 'test-dimension'; - const dimensionValue = 'test-value'; - const defaultDimensions: { [key: string]: string } = {}; - for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(initialDefaultDimensions).length); i++) { - defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; - } - - // Act & Assess - expect(() => { - metrics.setDefaultDimensions(defaultDimensions); - }).toThrowError('Max dimension count hit'); - - }); - - }); - - describe('Method: clearDefaultDimensions', () => { - - test('it should clear all default dimensions', () => { + const metricName = 'test-metric'; - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.setDefaultDimensions({ 'foo': 'bar' }); - - // Act - metrics.clearDefaultDimensions(); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: {} - })); - - }); - }); - - describe('Method: addMetadata', () => { - - test('it should add metadata', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - - // Act - metrics.addMetadata('foo', 'bar'); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - metadata: { 'foo': 'bar' } - })); - - }); - - test('it should update metadata value if added again', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - // Act - metrics.addMetadata('foo', 'bar'); - metrics.addMetadata('foo', 'baz'); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - metadata: { 'foo': 'baz' } - })); - - }); - }); - - describe('Method: clearDimensions', () => { + metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.clearMetrics(); - test('it should clear all dimensions', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addDimension('foo', 'bar'); - - // Act - metrics.clearDimensions(); - // Assess expect(metrics).toEqual(expect.objectContaining({ - dimensions: {} + storedMetrics: {}, })); - - }); - - test('it should not clear default dimensions', () => { - // Prepare - const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'prod' } }); - metrics.addDimension('foo', 'bar'); - - // Act - metrics.clearDimensions(); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - dimensions: {}, - defaultDimensions: { - 'environment': 'prod', - 'service': 'service_undefined' - } - })); - }); - - }); - - describe('Method: clearMetadata', () => { - test('it should clear all metadata', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetadata('foo', 'bar'); - metrics.addMetadata('test', 'baz'); - - // Act - metrics.clearMetadata(); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - metadata: {} - })); - - }); - - }); - - describe('Method: singleMetric', () => { - - test('it should return a single Metric object', () => { - - // Prepare - const defaultDimensions = { - 'foo': 'bar', - 'service': 'order' - }; - const metrics: Metrics = createMetrics({ - namespace: TEST_NAMESPACE, - defaultDimensions, - singleMetric: false - }); - - // Act - const singleMetric = metrics.singleMetric(); - - //Asses - expect(singleMetric).toEqual(expect.objectContaining({ - isSingleMetric: true, - namespace: TEST_NAMESPACE, - defaultDimensions - })); - - }); - - }); - - describe('Method: throwOnEmptyMetrics', () => { - - test('it should set the throwOnEmptyMetrics flag to true', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - - // Act - metrics.throwOnEmptyMetrics(); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - shouldThrowOnEmptyMetrics: true - })); - - }); - }); - - describe('Method: setFunctionName', () => { - - test('it should set the function name', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - - // Act - metrics.setFunctionName('test-function'); - - // Assess - expect(metrics).toEqual(expect.objectContaining({ - functionName: 'test-function' - })); - - }); - }); - describe('Method: logMetrics', () => { const expectedReturnValue = 'Lambda invoked!'; @@ -823,51 +748,158 @@ describe('Class: Metrics', () => { }); - describe('Method: serializeMetrics', () => { - - const defaultServiceName = 'service_undefined'; - - test('it should print warning, if no namespace provided in constructor or environment variable', () => { - + describe('Methods: publishStoredMetrics', () => { + + test('it should console warning if no metrics are added', () => { + // Prepare - process.env.POWERTOOLS_METRICS_NAMESPACE = ''; - const metrics: Metrics = new Metrics(); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); - // Act - metrics.serializeMetrics(); + // Act + metrics.publishStoredMetrics(); // Assess - expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); - delete process.env.POWERTOOLS_METRICS_NAMESPACE; - + expect(consoleWarnSpy).toBeCalledTimes(1); + expect(consoleWarnSpy).toBeCalledWith( + 'No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using \'throwOnEmptyMetrics\'', + ); + }); - test('it should return right object compliant with Cloudwatch EMF', () => { - + test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { + // Prepare - const metrics: Metrics = createMetrics({ - namespace: 'test-namespace', - serviceName: 'test-service', - defaultDimensions: { - 'environment': 'dev' - } - }); - - // Act - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.addMetric('successfulBooking', MetricUnits.Count, 3); - metrics.addMetric('failedBooking', MetricUnits.Count, 1, MetricResolution.High); - const loggedData = metrics.serializeMetrics(); - - // Assess - expect(loggedData).toEqual( - { - '_aws': { - 'Timestamp': mockDate.getTime(), - 'CloudWatchMetrics': [ - { - 'Namespace': 'test-namespace', + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); + const mockData: EmfOutput = { + '_aws': { + 'Timestamp': 1466424490000, + 'CloudWatchMetrics': [ + { + 'Namespace': 'test', + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': 'test-metrics', + 'Unit': MetricUnits.Count + } + ] + } + ] + }, + 'service': 'service_undefined', + 'test-metrics': 10 + }; + const serializeMetricsSpy = jest.spyOn(metrics, 'serializeMetrics').mockImplementation(() => mockData); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(serializeMetricsSpy).toBeCalledTimes(1); + expect(consoleLogSpy).toBeCalledTimes(1); + expect(consoleLogSpy).toBeCalledWith(JSON.stringify(mockData)); + + }); + + test('it should call clearMetrics function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearMetricsSpy).toBeCalledTimes(1); + + }); + + test('it should call clearDimensions function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearDimensionsSpy).toBeCalledTimes(1); + + }); + + test('it should call clearMetadata function', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.addMetric('test-metrics', MetricUnits.Count, 10); + const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(clearMetadataSpy).toBeCalledTimes(1); + + }); + + }); + + describe('Method: serializeMetrics', () => { + + const defaultServiceName = 'service_undefined'; + + test('it should print warning, if no namespace provided in constructor or environment variable', () => { + + // Prepare + process.env.POWERTOOLS_METRICS_NAMESPACE = ''; + const metrics: Metrics = new Metrics(); + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + + // Act + metrics.serializeMetrics(); + + // Assess + expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); + delete process.env.POWERTOOLS_METRICS_NAMESPACE; + + }); + + test('it should return right object compliant with Cloudwatch EMF', () => { + + // Prepare + const metrics: Metrics = createMetrics({ + namespace: 'test-namespace', + serviceName: 'test-service', + defaultDimensions: { + 'environment': 'dev' + } + }); + + // Act + metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnits.Count, 3); + metrics.addMetric('failedBooking', MetricUnits.Count, 1, MetricResolution.High); + const loggedData = metrics.serializeMetrics(); + + // Assess + expect(loggedData).toEqual( + { + '_aws': { + 'Timestamp': mockDate.getTime(), + 'CloudWatchMetrics': [ + { + 'Namespace': 'test-namespace', 'Dimensions': [ [ 'service', @@ -1135,235 +1167,205 @@ describe('Class: Metrics', () => { }); - describe('Methods: publishStoredMetrics', () => { - - test('it should console warning if no metrics are added', () => { + describe('Method: setDefaultDimensions', () => { + test('it should set default dimensions when service name is not provided', () => { + // Prepare + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); - - // Act - metrics.publishStoredMetrics(); - + + // Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + // Assess - expect(consoleWarnSpy).toBeCalledTimes(1); - expect(consoleWarnSpy).toBeCalledWith( - 'No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using \'throwOnEmptyMetrics\'', - ); + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : 'service_undefined' } + })); }); - test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { - + test('it should set default dimensions when service name is provided', () => { + // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); - const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); - const mockData: EmfOutput = { - '_aws': { - 'Timestamp': 1466424490000, - 'CloudWatchMetrics': [ - { - 'Namespace': 'test', - 'Dimensions': [ - [ - 'service' - ] - ], - 'Metrics': [ - { - 'Name': 'test-metrics', - 'Unit': MetricUnits.Count - } - ] - } - ] - }, - 'service': 'service_undefined', - 'test-metrics': 10 + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', }; - const serializeMetricsSpy = jest.spyOn(metrics, 'serializeMetrics').mockImplementation(() => mockData); - - // Act - metrics.publishStoredMetrics(); - + const serviceName = 'test-service'; + const metrics: Metrics = createMetrics({ serviceName: serviceName }); + + // Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + // Assess - expect(serializeMetricsSpy).toBeCalledTimes(1); - expect(consoleLogSpy).toBeCalledTimes(1); - expect(consoleLogSpy).toBeCalledWith(JSON.stringify(mockData)); - + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName } + })); + }); - test('it should call clearMetrics function', () => { - + test('it should add default dimensions', () => { + // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); - const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const serviceName = 'test-service'; + const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, + serviceName, + defaultDimensions: { 'test-dimension': 'test-dimension-value' } + }); - // Act - metrics.publishStoredMetrics(); + // Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess - expect(clearMetricsSpy).toBeCalledTimes(1); - + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName , 'test-dimension': 'test-dimension-value' } + })); + }); - test('it should call clearDimensions function', () => { - + test('it should update already added default dimensions values', () => { + // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); - const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); - - // Act - metrics.publishStoredMetrics(); - + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; + const serviceName = 'test-service'; + const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, + serviceName, + defaultDimensions: { 'environment': 'dev' } + }); + + // Act + metrics.setDefaultDimensions(defaultDimensionsToBeAdded); + // Assess - expect(clearDimensionsSpy).toBeCalledTimes(1); - + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: { foo: 'bar', service: serviceName, 'environment': 'prod' } + })); + }); - test('it should call clearMetadata function', () => { - + test('it should throw error if number of dimensions reaches the maximum allowed', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); - const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); - - // Act - metrics.publishStoredMetrics(); - - // Assess - expect(clearMetadataSpy).toBeCalledTimes(1); - + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const defaultDimensions: { [key: string]: string } = {}; + for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { + defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.setDefaultDimensions(defaultDimensions); + }).toThrowError('Max dimension count hit'); + }); + test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { + + // Prepare + const initialDefaultDimensions: { [key: string]: string } = { + 'test-dimension': 'test-value', + 'environment': 'dev' + }; + const metrics: Metrics = createMetrics({ + namespace: TEST_NAMESPACE, + defaultDimensions: initialDefaultDimensions + }); + const dimensionName = 'test-dimension'; + const dimensionValue = 'test-value'; + const defaultDimensions: { [key: string]: string } = {}; + for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(initialDefaultDimensions).length); i++) { + defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; + } + + // Act & Assess + expect(() => { + metrics.setDefaultDimensions(defaultDimensions); + }).toThrowError('Max dimension count hit'); + + }); + }); - describe('Methods: captureColdStartMetric', () => { + describe('Method: setFunctionName', () => { - test('it should call addMetric with correct parameters', () => { - + test('it should set the function name', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); - - // Act - metrics.captureColdStartMetric(); - + + // Act + metrics.setFunctionName('test-function'); + // Assess - expect(singleMetricSpy).toBeCalledTimes(1); - expect(addMetricSpy).toBeCalledTimes(1); - expect(addMetricSpy).toBeCalledWith(COLD_START_METRIC, MetricUnits.Count, 1); - + expect(metrics).toEqual(expect.objectContaining({ + functionName: 'test-function' + })); + }); + + }); + + describe('Method: singleMetric', () => { + + test('it should return a single Metric object', () => { - test('it should call setDefaultDimensions with correct parameters', () => { - // Prepare - const defaultDimensions: Dimensions = { + const defaultDimensions = { 'foo': 'bar', 'service': 'order' }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, - defaultDimensions + defaultDimensions, + singleMetric: false }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); - - // Act - metrics.captureColdStartMetric(); - - // Assess - expect(singleMetricSpy).toBeCalledTimes(1); - expect(setDefaultDimensionsSpy).toBeCalledTimes(1); - expect(setDefaultDimensionsSpy).toBeCalledWith({ service: defaultDimensions.service }); - - }); - test('it should call setDefaultDimensions with correct parameters if not set', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); - - // Act - metrics.captureColdStartMetric(); - - // Assess - expect(singleMetricSpy).toBeCalledTimes(1); - expect(setDefaultDimensionsSpy).toBeCalledTimes(1); - expect(setDefaultDimensionsSpy).toBeCalledWith({ service: 'service_undefined' }); - - }); + // Act + const singleMetric = metrics.singleMetric(); + + //Asses + expect(singleMetric).toEqual(expect.objectContaining({ + isSingleMetric: true, + namespace: TEST_NAMESPACE, + defaultDimensions + })); - test('it should call addDimension, if functionName is set', () => { - - // Prepare - const functionName = 'cold-start'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.setFunctionName(functionName); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); - - // Act - metrics.captureColdStartMetric(); - - // Assess - expect(singleMetricSpy).toBeCalledTimes(1); - expect(addDimensionSpy).toBeCalledTimes(1); - expect(addDimensionSpy).toBeCalledWith('function_name', functionName); - }); - test('it should not call addDimension, if functionName is not set', () => { - - // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); - - // Act - metrics.captureColdStartMetric(); - - // Assess - expect(singleMetricSpy).toBeCalledTimes(1); - expect(addDimensionSpy).toBeCalledTimes(0); - - }); + }); - test('it should not call any function, if there is no cold start', () => { - + describe('Method: throwOnEmptyMetrics', () => { + + test('it should set the throwOnEmptyMetrics flag to true', () => { + // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - jest.spyOn(metrics, 'isColdStart').mockImplementation(() => false); + + // Act + metrics.throwOnEmptyMetrics(); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); - const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); - const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); - const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); - const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); - - // Act - metrics.captureColdStartMetric(); - // Assess - expect(singleMetricSpy).toBeCalledTimes(0); - expect(setDefaultDimensionsSpy).toBeCalledTimes(0); - expect(addDimensionSpy).toBeCalledTimes(0); - expect(addMetricSpy).toBeCalledTimes(0); - + expect(metrics).toEqual(expect.objectContaining({ + shouldThrowOnEmptyMetrics: true + })); + }); }); + }); From 995235c4a3d23cefd61ab0023f1820f9549cb245 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 14:56:58 +0600 Subject: [PATCH 65/95] fix: default namespace test for serializeMetrics function --- packages/metrics/tests/unit/Metrics.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index c7620b9c37..01315cbdd6 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -26,9 +26,6 @@ describe('Class: Metrics', () => { beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); - }); - - beforeAll(() => { dateSpy.mockClear(); process.env = { ...ENVIRONMENT_VARIABLES }; }); @@ -1050,9 +1047,10 @@ describe('Class: Metrics', () => { }); - test('it should use default namespace when not provided', () => { + test('if the namespace is not provided or is not present in the environment variable, it should use the default namespace', () => { // Prepare + process.env.POWERTOOLS_METRICS_NAMESPACE = ''; const metrics: Metrics = createMetrics(); // Act From 8f7302bfd64143aa33d03dae586436b74e698242 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 14:58:33 +0600 Subject: [PATCH 66/95] fix: remove redundant deletion of process env --- packages/metrics/tests/unit/Metrics.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 01315cbdd6..4e87ed4e7f 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -868,7 +868,6 @@ describe('Class: Metrics', () => { // Assess expect(consoleWarnSpy).toBeCalledWith('Namespace should be defined, default used'); - delete process.env.POWERTOOLS_METRICS_NAMESPACE; }); @@ -953,7 +952,6 @@ describe('Class: Metrics', () => { // Assess expect(loggedData.service).toEqual(serviceName); - delete process.env.POWERTOOLS_SERVICE_NAME; }); From 62ad51a9d108e7f1771b2d49f302625e0744734f Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 15:39:39 +0600 Subject: [PATCH 67/95] test: addDimension should update existing dimension value if same dimension is added again --- packages/metrics/tests/unit/Metrics.test.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 4e87ed4e7f..f39c14a800 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -51,7 +51,26 @@ describe('Class: Metrics', () => { }); - test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + test('it should update existing dimension value if same dimension is added again', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const dimensionName = 'test-dimension'; + + // Act + metrics.addDimension(dimensionName, 'test-value-1'); + metrics.addDimension(dimensionName, 'test-value-2'); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + dimensions: { + [dimensionName]: 'test-value-2' + } + })); + + }); + + test('it should throw error if the number of dimensions exceeds the maximum allowed', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); From 8dd3ed960501ee02bac503a8b483a5615c029e79 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:17:04 +0600 Subject: [PATCH 68/95] style: rephrase test description for addDimensions --- packages/metrics/tests/unit/Metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index f39c14a800..fc5c6b8b9e 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -126,7 +126,7 @@ describe('Class: Metrics', () => { }); - test('if same dimension is added again, it should update existing dimension value', () => { + test('it should update existing dimension value if same dimension is added again', () => { // Prepare const dimensionsToBeAdded: { [key: string]: string } = { From 940c58512db6a1d761daea3dc3aa012632e74bd5 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:17:40 +0600 Subject: [PATCH 69/95] style: rephrase test description for addMetadata --- packages/metrics/tests/unit/Metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index fc5c6b8b9e..24412c631e 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -205,7 +205,7 @@ describe('Class: Metrics', () => { }); - test('it should update metadata value if added again', () => { + test('it should update existing metadata value if same metadata is added again', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); From c376a44dac020380c59ae7153cdb06a1a9b28cfc Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:26:31 +0600 Subject: [PATCH 70/95] style: rephrase test description for addMetric --- packages/metrics/tests/unit/Metrics.test.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 24412c631e..1a50be58b3 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -224,7 +224,7 @@ describe('Class: Metrics', () => { describe('Method: addMetric', () => { - test('when called, it should store metrics', () => { + test('it should store metrics when called', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -244,9 +244,10 @@ describe('Class: Metrics', () => { } }, })); + }); - test('when called with multiple metric name, it should store multiple metrics', () => { + test('it should store multiple metrics when called with multiple metric name', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -279,9 +280,10 @@ describe('Class: Metrics', () => { } }, })); + }); - test('when called without resolution, it should store metrics with standard resolution', () => { + test('it should store metrics with standard resolution when called without resolution', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -307,9 +309,10 @@ describe('Class: Metrics', () => { } }, })); + }); - test('when trying to add metric with the same name multiple times, values should be grouped together in an array', () => { + test('it should group the metric values together in an array when trying to add same metric with different values', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -332,9 +335,10 @@ describe('Class: Metrics', () => { } }, })); + }); - test('when trying to add metric with the same name multiple times but with different unit, it will throw an error', () => { + test('it should throw an error when trying to add same metric with different unit', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -348,7 +352,7 @@ describe('Class: Metrics', () => { }); - test('it will publish metrics if stored metrics count has reached max metric size threshold', () => { + test('it should publish metrics if stored metrics count has reached max metric size threshold', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -365,7 +369,7 @@ describe('Class: Metrics', () => { }); - test('it will not publish metrics if stored metrics count has not reached max metric size threshold', () => { + test('it should not publish metrics if stored metrics count has not reached max metric size threshold', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -381,6 +385,7 @@ describe('Class: Metrics', () => { expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); }); + }); describe('Methods: captureColdStartMetric', () => { From 3c6ae18530c252d2402d65657ca0f8963a09cd0d Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:34:51 +0600 Subject: [PATCH 71/95] test: addMetric should publish metrics on every call if singleMetric is true --- packages/metrics/tests/unit/Metrics.test.ts | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 1a50be58b3..6bb0648592 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -386,6 +386,51 @@ describe('Class: Metrics', () => { }); + test('it should publish metrics on every call if singleMetric is set to true', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + + // Act + metrics.addMetric('test-metric-1', MetricUnits.Count, 1); + metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + + // Assess + expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(2); + + }); + + test('it should not publish metrics on every call if singleMetric is set to false', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: false }); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + + // Act + metrics.addMetric('test-metric-1', MetricUnits.Count, 1); + metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + + // Assess + expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); + + }); + + test('it should not publish metrics on every call if singleMetric is not provided', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + + // Act + metrics.addMetric('test-metric-1', MetricUnits.Count, 1); + metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + + // Assess + expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); + + }); + }); describe('Methods: captureColdStartMetric', () => { From a2e4065c9729e8138202047912694cc973bb9e79 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:43:18 +0600 Subject: [PATCH 72/95] style: rephrase test description for captureColdStartMetric --- packages/metrics/tests/unit/Metrics.test.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 6bb0648592..8c35c329ce 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -460,10 +460,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics: Metrics = createMetrics({ - namespace: TEST_NAMESPACE, - defaultDimensions - }); + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, defaultDimensions }); const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -478,7 +475,7 @@ describe('Class: Metrics', () => { }); - test('it should call setDefaultDimensions with correct parameters if not set', () => { + test('it should call setDefaultDimensions with correct parameters when defaultDimensions are not set', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -499,7 +496,7 @@ describe('Class: Metrics', () => { test('it should call addDimension, if functionName is set', () => { // Prepare - const functionName = 'cold-start'; + const functionName = 'coldStart'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); metrics.setFunctionName(functionName); const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); From 5de965f25970b6d79c2167a56598e6a205d02569 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:50:13 +0600 Subject: [PATCH 73/95] test: clearDefaultDimensions should only clear default dimensions --- packages/metrics/tests/unit/Metrics.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 8c35c329ce..dd710e6ad1 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -572,6 +572,27 @@ describe('Class: Metrics', () => { })); }); + + test('it should only clear default dimensions', () => { + + // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics.setDefaultDimensions({ 'foo': 'bar' }); + metrics.addDimension('environment', 'dev'); + + // Act + metrics.clearDefaultDimensions(); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + defaultDimensions: {}, + dimensions: { + 'environment': 'dev' + } + })); + + }); + }); describe('Method: clearDimensions', () => { From ad81018f86fd9c31388c7521f7b68406383a9c72 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:51:13 +0600 Subject: [PATCH 74/95] style: rephrase test description of clearDimensions --- packages/metrics/tests/unit/Metrics.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index dd710e6ad1..427d439daa 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -613,10 +613,10 @@ describe('Class: Metrics', () => { }); - test('it should not clear default dimensions', () => { + test('it should only clear dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'prod' } }); + const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'dev' } }); metrics.addDimension('foo', 'bar'); // Act @@ -626,7 +626,7 @@ describe('Class: Metrics', () => { expect(metrics).toEqual(expect.objectContaining({ dimensions: {}, defaultDimensions: { - 'environment': 'prod', + 'environment': 'dev', 'service': 'service_undefined' } })); From 880fcc254c468d2fae65fb2755630a4b0fc5a88e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 16:52:01 +0600 Subject: [PATCH 75/95] style: rephrase test description of clearMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 427d439daa..0ee2c68552 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -658,7 +658,7 @@ describe('Class: Metrics', () => { describe('Method: clearMetrics', () => { - test('when called, it should clear stored metrics', () => { + test('it should clear stored metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); From d68e55f75f9a57a0c82113f5dd4eb15c8e2784ac Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Sun, 16 Apr 2023 21:33:21 +0600 Subject: [PATCH 76/95] refactor: common functionality inside beforeEach for logMetrics test --- packages/metrics/tests/unit/Metrics.test.ts | 28 ++++++++++----------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 0ee2c68552..d6fdf542e8 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -681,14 +681,23 @@ describe('Class: Metrics', () => { const expectedReturnValue = 'Lambda invoked!'; const testMetric = 'successfulBooking'; + let metrics: Metrics; + let publishStoredMetricsSpy: jest.SpyInstance; + let addMetricSpy: jest.SpyInstance; + let captureColdStartMetricSpy: jest.SpyInstance; + let setDefaultDimensionsSpy: jest.SpyInstance; + + beforeEach(() => { + metrics = createMetrics({ namespace: TEST_NAMESPACE }); + publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); + addMetricSpy = jest.spyOn(metrics, 'addMetric'); + captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); + setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); + }); test('it should log metrics', async () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); - const addMetricSpy = jest.spyOn(metrics, 'addMetric'); - const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); class LambdaFunction implements LambdaInterface { @metrics.logMetrics() @@ -718,10 +727,6 @@ describe('Class: Metrics', () => { test('it should capture cold start metrics, if passed in the options as true', async () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); - const addMetricSpy = jest.spyOn(metrics, 'addMetric'); - const captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ captureColdStartMetric: true }) @@ -751,7 +756,6 @@ describe('Class: Metrics', () => { test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ throwOnEmptyMetrics: true }) @@ -777,11 +781,6 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); - const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); - const addMetricSpy = jest.spyOn(metrics, 'addMetric'); - class LambdaFunction implements LambdaInterface { @metrics.logMetrics({ defaultDimensions }) @@ -810,7 +809,6 @@ describe('Class: Metrics', () => { test('it should throw error if lambda handler throws any error', async () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const errorMessage = 'Unexpected error occurred!'; class LambdaFunction implements LambdaInterface { From c749956f0aaaf8128d0f631963d4eef5023d5ed1 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 17 Apr 2023 22:09:36 +0600 Subject: [PATCH 77/95] refactor: publish metrics test for logMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d6fdf542e8..bd5648e859 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -685,6 +685,7 @@ describe('Class: Metrics', () => { let publishStoredMetricsSpy: jest.SpyInstance; let addMetricSpy: jest.SpyInstance; let captureColdStartMetricSpy: jest.SpyInstance; + let throwOnEmptyMetricsSpy: jest.SpyInstance; let setDefaultDimensionsSpy: jest.SpyInstance; beforeEach(() => { @@ -692,10 +693,11 @@ describe('Class: Metrics', () => { publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); addMetricSpy = jest.spyOn(metrics, 'addMetric'); captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); + throwOnEmptyMetricsSpy = jest.spyOn(metrics, 'throwOnEmptyMetrics'); setDefaultDimensionsSpy = jest.spyOn(metrics, 'setDefaultDimensions'); }); - test('it should log metrics', async () => { + test('it should execute lambda function & publish stored metrics', async () => { // Prepare class LambdaFunction implements LambdaInterface { @@ -718,9 +720,11 @@ describe('Class: Metrics', () => { // Assess expect(actualResult).toEqual(expectedReturnValue); - expect(captureColdStartMetricSpy).not.toBeCalled(); expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); expect(publishStoredMetricsSpy).toBeCalledTimes(1); + expect(captureColdStartMetricSpy).not.toBeCalled(); + expect(throwOnEmptyMetricsSpy).not.toBeCalled(); + expect(setDefaultDimensionsSpy).not.toBeCalled(); }); From 8148b77c6e4fe6a0ab07880f09cda564a0e31d0a Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 17 Apr 2023 22:10:32 +0600 Subject: [PATCH 78/95] refactor: capture cold start test for logMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index bd5648e859..4ef9411fec 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -751,9 +751,11 @@ describe('Class: Metrics', () => { // Assess expect(actualResult).toEqual(expectedReturnValue); - expect(captureColdStartMetricSpy).toBeCalledTimes(1); expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(captureColdStartMetricSpy).toBeCalledTimes(1); expect(publishStoredMetricsSpy).toBeCalledTimes(1); + expect(throwOnEmptyMetricsSpy).not.toBeCalled(); + expect(setDefaultDimensionsSpy).not.toBeCalled(); }); From 5aa15db6261ab48a4f3781a701e0ed08da5c0764 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 17 Apr 2023 22:14:01 +0600 Subject: [PATCH 79/95] refactor: call throwOnEmptyMetrics test for logMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 4ef9411fec..1362ac702f 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -759,7 +759,7 @@ describe('Class: Metrics', () => { }); - test('it should throw error if no metrics are added and throwOnEmptyMetrics is set to true', async () => { + test('it should call throwOnEmptyMetrics, if passed in the options as true', async () => { // Prepare class LambdaFunction implements LambdaInterface { @@ -768,6 +768,8 @@ describe('Class: Metrics', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore public async handler(_event: TEvent, _context: Context): Promise { + metrics.addMetric(testMetric, MetricUnits.Count, 1); + return expectedReturnValue; } @@ -775,8 +777,16 @@ describe('Class: Metrics', () => { const handlerClass = new LambdaFunction(); const handler = handlerClass.handler.bind(handlerClass); - // Act & Assess - await expect(handler(event, context)).rejects.toThrowError('The number of metrics recorded must be higher than zero'); + // Act + const actualResult = await handler(event, context); + + // Assess + expect(actualResult).toEqual(expectedReturnValue); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(throwOnEmptyMetricsSpy).toBeCalledTimes(1); + expect(publishStoredMetricsSpy).toBeCalledTimes(1); + expect(captureColdStartMetricSpy).not.toBeCalled(); + expect(setDefaultDimensionsSpy).not.toBeCalled(); }); From dbd8a47e9f253a5d8da331cdf88fc84e452ae1db Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 17 Apr 2023 22:16:05 +0600 Subject: [PATCH 80/95] refactor: set default dimensions test for logMetrics --- packages/metrics/tests/unit/Metrics.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 1362ac702f..476307895c 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -813,12 +813,15 @@ describe('Class: Metrics', () => { const handler = handlerClass.handler.bind(handlerClass); // Act - await handler(event, context); + const actualResult = await handler(event, context); // Assess - expect(setDefaultDimensionsSpy).toHaveBeenNthCalledWith(1, defaultDimensions); + expect(actualResult).toEqual(expectedReturnValue); expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(setDefaultDimensionsSpy).toHaveBeenNthCalledWith(1, defaultDimensions); expect(publishStoredMetricsSpy).toBeCalledTimes(1); + expect(throwOnEmptyMetricsSpy).not.toBeCalled(); + expect(captureColdStartMetricSpy).not.toBeCalled(); }); From 35b1727333bf9334491aa8ce1b9646591509ddf7 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Mon, 17 Apr 2023 22:40:33 +0600 Subject: [PATCH 81/95] refactor: separate decorator lambda function generation for logMetrics test --- .../metrics/tests/helpers/metricsUtils.ts | 26 ++++- packages/metrics/tests/unit/Metrics.test.ts | 95 +++++-------------- 2 files changed, 46 insertions(+), 75 deletions(-) diff --git a/packages/metrics/tests/helpers/metricsUtils.ts b/packages/metrics/tests/helpers/metricsUtils.ts index d585b777a0..27fd8cac4f 100644 --- a/packages/metrics/tests/helpers/metricsUtils.ts +++ b/packages/metrics/tests/helpers/metricsUtils.ts @@ -1,7 +1,11 @@ import { CloudWatch } from 'aws-sdk'; import promiseRetry from 'promise-retry'; +import { Metrics } from '../../src'; +import { ExtraOptions, MetricUnits } from '../../src/types'; +import { Context, Handler } from 'aws-lambda'; +import { LambdaInterface } from '@aws-lambda-powertools/commons'; -const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metric: string, expectedMetrics: number): Promise => { +export const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metric: string, expectedMetrics: number): Promise => { const retryOptions = { retries: 20, minTimeout: 5_000, maxTimeout: 10_000, factor: 1.25 }; return promiseRetry(async (retry: (err?: Error) => never, _: number) => { @@ -21,4 +25,22 @@ const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metri }, retryOptions); }; -export { getMetrics }; \ No newline at end of file +export const setupDecoratorLambdaHandler = (metrics: Metrics, options: ExtraOptions = {}): Handler => { + + class LambdaFunction implements LambdaInterface { + @metrics.logMetrics(options) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + public async handler(_event: TEvent, _context: Context): Promise { + metrics.addMetric('decorator-lambda-test-metric', MetricUnits.Count, 1); + + return 'Lambda invoked!'; + } + } + + const handlerClass = new LambdaFunction(); + const handler = handlerClass.handler.bind(handlerClass); + + return handler; +}; + diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 476307895c..a39e8d6c58 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -9,9 +9,10 @@ import { Events as dummyEvent } from '@aws-lambda-powertools/commons'; import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; -import { Context } from 'aws-lambda'; +import { Context, Handler } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; import { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; +import { setupDecoratorLambdaHandler } from '../helpers/metricsUtils'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -679,14 +680,14 @@ describe('Class: Metrics', () => { describe('Method: logMetrics', () => { - const expectedReturnValue = 'Lambda invoked!'; - const testMetric = 'successfulBooking'; let metrics: Metrics; let publishStoredMetricsSpy: jest.SpyInstance; let addMetricSpy: jest.SpyInstance; let captureColdStartMetricSpy: jest.SpyInstance; let throwOnEmptyMetricsSpy: jest.SpyInstance; let setDefaultDimensionsSpy: jest.SpyInstance; + const decoratorLambdaExpectedReturnValue = 'Lambda invoked!'; + const decoratorLambdaMetric= 'decorator-lambda-test-metric'; beforeEach(() => { metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -700,27 +701,14 @@ describe('Class: Metrics', () => { test('it should execute lambda function & publish stored metrics', async () => { // Prepare - class LambdaFunction implements LambdaInterface { - - @metrics.logMetrics() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler(_event: TEvent, _context: Context): Promise { - metrics.addMetric(testMetric, MetricUnits.Count, 1); - - return expectedReturnValue; - } - - } - const handlerClass = new LambdaFunction(); - const handler = handlerClass.handler.bind(handlerClass); + const handler: Handler = setupDecoratorLambdaHandler(metrics); // Act - const actualResult = await handler(event, context); + const actualResult = await handler(event, context, () => console.log('callback')); // Assess - expect(actualResult).toEqual(expectedReturnValue); - expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(actualResult).toEqual(decoratorLambdaExpectedReturnValue); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, decoratorLambdaMetric, MetricUnits.Count, 1); expect(publishStoredMetricsSpy).toBeCalledTimes(1); expect(captureColdStartMetricSpy).not.toBeCalled(); expect(throwOnEmptyMetricsSpy).not.toBeCalled(); @@ -731,27 +719,14 @@ describe('Class: Metrics', () => { test('it should capture cold start metrics, if passed in the options as true', async () => { // Prepare - class LambdaFunction implements LambdaInterface { - - @metrics.logMetrics({ captureColdStartMetric: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler(_event: TEvent, _context: Context): Promise { - metrics.addMetric(testMetric, MetricUnits.Count, 1); - - return expectedReturnValue; - } - - } - const handlerClass = new LambdaFunction(); - const handler = handlerClass.handler.bind(handlerClass); + const handler: Handler = setupDecoratorLambdaHandler(metrics, { captureColdStartMetric: true }); // Act - const actualResult = await handler(event, context); + const actualResult = await handler(event, context, () => console.log('callback')); // Assess - expect(actualResult).toEqual(expectedReturnValue); - expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(actualResult).toEqual(decoratorLambdaExpectedReturnValue); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, decoratorLambdaMetric, MetricUnits.Count, 1); expect(captureColdStartMetricSpy).toBeCalledTimes(1); expect(publishStoredMetricsSpy).toBeCalledTimes(1); expect(throwOnEmptyMetricsSpy).not.toBeCalled(); @@ -762,27 +737,14 @@ describe('Class: Metrics', () => { test('it should call throwOnEmptyMetrics, if passed in the options as true', async () => { // Prepare - class LambdaFunction implements LambdaInterface { - - @metrics.logMetrics({ throwOnEmptyMetrics: true }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler(_event: TEvent, _context: Context): Promise { - metrics.addMetric(testMetric, MetricUnits.Count, 1); - - return expectedReturnValue; - } - - } - const handlerClass = new LambdaFunction(); - const handler = handlerClass.handler.bind(handlerClass); - + const handler: Handler = setupDecoratorLambdaHandler(metrics, { throwOnEmptyMetrics: true }); + // Act - const actualResult = await handler(event, context); + const actualResult = await handler(event, context, () => console.log('callback')); // Assess - expect(actualResult).toEqual(expectedReturnValue); - expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(actualResult).toEqual(decoratorLambdaExpectedReturnValue); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, decoratorLambdaMetric, MetricUnits.Count, 1); expect(throwOnEmptyMetricsSpy).toBeCalledTimes(1); expect(publishStoredMetricsSpy).toBeCalledTimes(1); expect(captureColdStartMetricSpy).not.toBeCalled(); @@ -790,34 +752,21 @@ describe('Class: Metrics', () => { }); - test('it should set default dimensions if passed in the options', async () => { + test('it should set default dimensions if passed in the options as true', async () => { // Prepare const defaultDimensions = { 'foo': 'bar', 'service': 'order' }; - class LambdaFunction implements LambdaInterface { - - @metrics.logMetrics({ defaultDimensions }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public async handler(_event: TEvent, _context: Context): Promise { - metrics.addMetric(testMetric, MetricUnits.Count, 1); - - return expectedReturnValue; - } - - } - const handlerClass = new LambdaFunction(); - const handler = handlerClass.handler.bind(handlerClass); + const handler: Handler = setupDecoratorLambdaHandler(metrics, { defaultDimensions }); // Act - const actualResult = await handler(event, context); + const actualResult = await handler(event, context, () => console.log('callback')); // Assess - expect(actualResult).toEqual(expectedReturnValue); - expect(addMetricSpy).toHaveBeenNthCalledWith(1, testMetric, MetricUnits.Count, 1); + expect(actualResult).toEqual(decoratorLambdaExpectedReturnValue); + expect(addMetricSpy).toHaveBeenNthCalledWith(1, decoratorLambdaMetric, MetricUnits.Count, 1); expect(setDefaultDimensionsSpy).toHaveBeenNthCalledWith(1, defaultDimensions); expect(publishStoredMetricsSpy).toBeCalledTimes(1); expect(throwOnEmptyMetricsSpy).not.toBeCalled(); From 23c474bd95b3c790bd08abafa98a6717b29922b5 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Tue, 18 Apr 2023 21:47:52 +0600 Subject: [PATCH 82/95] refactor: publishStoredMetrics tests --- packages/metrics/tests/unit/Metrics.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index a39e8d6c58..8b3f3edf5b 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -800,7 +800,7 @@ describe('Class: Metrics', () => { describe('Methods: publishStoredMetrics', () => { - test('it should console warning if no metrics are added', () => { + test('it should log warning if no metrics are added & throwOnEmptyMetrics is false', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -825,7 +825,7 @@ describe('Class: Metrics', () => { const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); const mockData: EmfOutput = { '_aws': { - 'Timestamp': 1466424490000, + 'Timestamp': mockDate.getTime(), 'CloudWatchMetrics': [ { 'Namespace': 'test', From 562f8d54768dfeb0f4bc02ddb5120f7617adda10 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Tue, 18 Apr 2023 22:51:37 +0600 Subject: [PATCH 83/95] improv: test return values for serializeMetrics. This will help visualize the output of serializeMetrics for different inputs --- packages/metrics/tests/unit/Metrics.test.ts | 393 ++++++++++++++++++-- 1 file changed, 362 insertions(+), 31 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 8b3f3edf5b..354e5c3c45 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -821,7 +821,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnits.Count, 10); const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); const mockData: EmfOutput = { '_aws': { @@ -836,7 +836,7 @@ describe('Class: Metrics', () => { ], 'Metrics': [ { - 'Name': 'test-metrics', + 'Name': 'test-metric', 'Unit': MetricUnits.Count } ] @@ -844,7 +844,7 @@ describe('Class: Metrics', () => { ] }, 'service': 'service_undefined', - 'test-metrics': 10 + 'test-metric': 10 }; const serializeMetricsSpy = jest.spyOn(metrics, 'serializeMetrics').mockImplementation(() => mockData); @@ -862,7 +862,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); // Act @@ -877,7 +877,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); // Act @@ -892,7 +892,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); // Act @@ -958,11 +958,11 @@ describe('Class: Metrics', () => { 'Metrics': [ { 'Name': 'successfulBooking', - 'Unit': 'Count' + 'Unit': MetricUnits.Count }, { 'Name': 'failedBooking', - 'Unit': 'Count', + 'Unit': MetricUnits.Count, 'StorageResolution': 1 } ] @@ -981,30 +981,78 @@ describe('Class: Metrics', () => { // Prepare const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ serviceName:serviceName }); + const testMetric = 'test-metric'; + const metrics: Metrics = createMetrics({ serviceName: serviceName, namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData.service).toEqual(serviceName); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': serviceName, + [testMetric]: 10 + }); }); - test('it should log service dimension correctly from env var when not passed', () => { + test('it should log service dimension correctly using environment variable when not specified in constructor', () => { // Prepare const serviceName = 'hello-world-service'; process.env.POWERTOOLS_SERVICE_NAME = serviceName; + const testMetric = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData.service).toEqual(serviceName); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': serviceName, + [testMetric]: 10 + }); }); @@ -1015,10 +1063,11 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'env': 'dev' }; - const metrics: Metrics = createMetrics({ defaultDimensions: additionalDimensions }); + const testMetric = 'test-metric'; + const metrics: Metrics = createMetrics({ defaultDimensions: additionalDimensions, namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1026,17 +1075,45 @@ describe('Class: Metrics', () => { expect(loggedData.service).toEqual(defaultServiceName); expect(loggedData.foo).toEqual(additionalDimensions.foo); expect(loggedData.env).toEqual(additionalDimensions.env); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service', + 'foo', + 'env' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10, + 'env': 'dev', + 'foo': 'bar', + }); }); test('it should log additional dimensions correctly', () => { // Prepare + const testMetric = 'test-metric'; const additionalDimension = { name: 'metric2', value: 'metric2Value' }; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); + metrics.addMetric('test-metric', MetricUnits.Count, 10, MetricResolution.High); metrics.addDimension(additionalDimension.name, additionalDimension.value); const loggedData = metrics.serializeMetrics(); @@ -1044,12 +1121,39 @@ describe('Class: Metrics', () => { expect(loggedData._aws.CloudWatchMetrics[0].Dimensions[0].length).toEqual(2); expect(loggedData.service).toEqual(defaultServiceName); expect(loggedData[additionalDimension.name]).toEqual(additionalDimension.value); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service', + 'metric2' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'StorageResolution': 1, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10, + 'metric2': 'metric2Value' + }); }); test('it should log additional bulk dimensions correctly', () => { // Prepare + const testMetric = 'test-metric'; const additionalDimensions: { [key: string]: string } = { metric2: 'metric2Value', metric3: 'metric3Value' @@ -1057,7 +1161,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10, MetricResolution.High); + metrics.addMetric(testMetric, MetricUnits.Count, 10, MetricResolution.High); metrics.addDimensions(additionalDimensions); const loggedData = metrics.serializeMetrics(); @@ -1067,21 +1171,74 @@ describe('Class: Metrics', () => { Object.keys(additionalDimensions).forEach((key) => { expect(loggedData[key]).toEqual(additionalDimensions[key]); }); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service', + 'metric2', + 'metric3', + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'StorageResolution': 1, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10, + 'metric2': 'metric2Value', + 'metric3': 'metric3Value' + }); }); test('it should log metadata correctly', () => { // Prepare + const testMetric = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); metrics.addMetadata('foo', 'bar'); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData.foo).toEqual('bar'); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10, + 'foo': 'bar' + }); }); @@ -1098,39 +1255,87 @@ describe('Class: Metrics', () => { }); - test('if the namespace is not provided or is not present in the environment variable, it should use the default namespace', () => { + test('it should use the default namespace when no namespace is provided in constructor or found in environment variable', () => { // Prepare process.env.POWERTOOLS_METRICS_NAMESPACE = ''; + const testMetric = 'test-metric'; const metrics: Metrics = createMetrics(); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(DEFAULT_NAMESPACE); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': DEFAULT_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10 + }); }); test('it should use namespace provided in constructor', () => { // Prepare + const testMetric = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metrics', MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnits.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData._aws.CloudWatchMetrics[0].Namespace).toEqual(TEST_NAMESPACE); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': testMetric, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [testMetric]: 10 + }); }); test('it should contain a metric value if added once', () => { // Prepare - const metricName = 'test-metrics'; + const metricName = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act @@ -1139,14 +1344,37 @@ describe('Class: Metrics', () => { // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData['test-metrics']).toEqual(10); + expect(loggedData[metricName]).toEqual(10); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': metricName, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [metricName]: 10 + }); }); test('it should convert metric value with the same name and unit to array if added multiple times', () => { // Prepare - const metricName = 'test-metrics'; + const metricName = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act @@ -1157,14 +1385,37 @@ describe('Class: Metrics', () => { // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); expect(loggedData[metricName]).toEqual([ 10, 20 ]); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': metricName, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [metricName]: [ 10, 20 ] + }); }); test('it should create multiple metric values if added multiple times', () => { // Prepare - const metricName1 = 'test-metrics'; - const metricName2 = 'test-metrics-2'; + const metricName1 = 'test-metric-1'; + const metricName2 = 'test-metric-2'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act @@ -1176,13 +1427,41 @@ describe('Class: Metrics', () => { expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); expect(loggedData[metricName1]).toEqual(10); expect(loggedData[metricName2]).toEqual(20); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': metricName1, + 'Unit': MetricUnits.Count + }, + { + 'Name': metricName2, + 'Unit': MetricUnits.Seconds + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [metricName1]: 10, + [metricName2]: 20 + }); }); test('it should not contain `StorageResolution` as key for non-high resolution metrics', () => { // Prepare - const metricName = 'test-metrics'; + const metricName = 'test-metric'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act @@ -1191,26 +1470,78 @@ describe('Class: Metrics', () => { // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(1); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); + expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': metricName, + 'Unit': MetricUnits.Count + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [metricName]: 10 + }); }); test('it should contain `StorageResolution` as key & high metric resolution as value for high resolution metrics', () => { // Prepare - const metricName = 'test-metrics'; - const metricName2 = 'test-metrics-2'; + const metricName1 = 'test-metric'; + const metricName2 = 'test-metric-2'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName, MetricUnits.Count, 10); + metrics.addMetric(metricName1, MetricUnits.Count, 10); metrics.addMetric(metricName2, MetricUnits.Seconds, 10, MetricResolution.High); const loggedData = metrics.serializeMetrics(); // Assess expect(loggedData._aws.CloudWatchMetrics[0].Metrics.length).toBe(2); expect(loggedData._aws.CloudWatchMetrics[0].Metrics[0].StorageResolution).toBeUndefined(); - expect(loggedData._aws.CloudWatchMetrics[0].Metrics[1].StorageResolution).toEqual(MetricResolution.High); + expect(loggedData._aws.CloudWatchMetrics[0].Metrics[1].StorageResolution).toEqual(MetricResolution.High); + expect(loggedData).toEqual({ + '_aws': { + 'CloudWatchMetrics': [ + { + 'Dimensions': [ + [ + 'service' + ] + ], + 'Metrics': [ + { + 'Name': metricName1, + 'Unit': MetricUnits.Count + }, + { + 'Name': metricName2, + 'StorageResolution': 1, + 'Unit': MetricUnits.Seconds + } + ], + 'Namespace': TEST_NAMESPACE + } + ], + 'Timestamp': mockDate.getTime() + }, + 'service': 'service_undefined', + [metricName1]: 10, + [metricName2]: 10 + }); }); From 9ea8d0405097ba1efc1211a21650465cc4b86e9b Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Tue, 18 Apr 2023 23:01:37 +0600 Subject: [PATCH 84/95] refactor: tests for setDefaultDimensions --- packages/metrics/tests/unit/Metrics.test.ts | 60 +++++++++++++-------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 354e5c3c45..739aed3183 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1548,42 +1548,48 @@ describe('Class: Metrics', () => { }); describe('Method: setDefaultDimensions', () => { - - test('it should set default dimensions when service name is not provided', () => { + + test('it should set default dimensions correctly when service name is provided', () => { // Prepare + const serviceName = 'test-service'; + const metrics: Metrics = createMetrics({ serviceName: serviceName }); const defaultDimensionsToBeAdded = { - 'environment': 'prod', + 'environment': 'dev', 'foo': 'bar', }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : 'service_undefined' } + defaultDimensions: { + ...defaultDimensionsToBeAdded, + service: serviceName + } })); }); - - test('it should set default dimensions when service name is provided', () => { + + test('it should set default dimensions correctly when service name is not provided', () => { // Prepare + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const defaultDimensionsToBeAdded = { - 'environment': 'prod', + 'environment': 'dev', 'foo': 'bar', }; - const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ serviceName: serviceName }); // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName } + defaultDimensions: { + ...defaultDimensionsToBeAdded, + service: 'service_undefined' + } })); }); @@ -1591,23 +1597,27 @@ describe('Class: Metrics', () => { test('it should add default dimensions', () => { // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, serviceName, defaultDimensions: { 'test-dimension': 'test-dimension-value' } }); + const defaultDimensionsToBeAdded = { + 'environment': 'dev', + 'foo': 'bar', + }; // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { ...defaultDimensionsToBeAdded, service : serviceName , 'test-dimension': 'test-dimension-value' } + defaultDimensions: { + ...defaultDimensionsToBeAdded, + service: serviceName, + 'test-dimension': 'test-dimension-value' + } })); }); @@ -1615,23 +1625,29 @@ describe('Class: Metrics', () => { test('it should update already added default dimensions values', () => { // Prepare - const defaultDimensionsToBeAdded = { - 'environment': 'prod', - 'foo': 'bar', - }; const serviceName = 'test-service'; const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, serviceName, - defaultDimensions: { 'environment': 'dev' } + defaultDimensions: { + environment: 'dev' + } }); + const defaultDimensionsToBeAdded = { + 'environment': 'prod', + 'foo': 'bar', + }; // Act metrics.setDefaultDimensions(defaultDimensionsToBeAdded); // Assess expect(metrics).toEqual(expect.objectContaining({ - defaultDimensions: { foo: 'bar', service: serviceName, 'environment': 'prod' } + defaultDimensions: { + foo: 'bar', + service: serviceName, + environment: 'prod' + } })); }); From a402e156384e61bc416d664c2f5192d22504178d Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 12:28:16 +0600 Subject: [PATCH 85/95] style: spacing in Metrics class --- packages/metrics/src/Metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index 9485a7f918..ad5e64345b 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -2,7 +2,7 @@ import { Callback, Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import { MetricsInterface } from '.'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; -import { MAX_DIMENSION_COUNT,MAX_METRICS_SIZE,DEFAULT_NAMESPACE, COLD_START_METRIC } from './constants'; +import { MAX_DIMENSION_COUNT, MAX_METRICS_SIZE, DEFAULT_NAMESPACE, COLD_START_METRIC } from './constants'; import { MetricsOptions, Dimensions, From 80e4c3b027a1d032fb309aa8b8bf60417feff009 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 12:32:34 +0600 Subject: [PATCH 86/95] refactor: function export for metricsUtils --- packages/metrics/tests/helpers/metricsUtils.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/helpers/metricsUtils.ts b/packages/metrics/tests/helpers/metricsUtils.ts index 27fd8cac4f..77ad46b345 100644 --- a/packages/metrics/tests/helpers/metricsUtils.ts +++ b/packages/metrics/tests/helpers/metricsUtils.ts @@ -5,7 +5,7 @@ import { ExtraOptions, MetricUnits } from '../../src/types'; import { Context, Handler } from 'aws-lambda'; import { LambdaInterface } from '@aws-lambda-powertools/commons'; -export const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metric: string, expectedMetrics: number): Promise => { +const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metric: string, expectedMetrics: number): Promise => { const retryOptions = { retries: 20, minTimeout: 5_000, maxTimeout: 10_000, factor: 1.25 }; return promiseRetry(async (retry: (err?: Error) => never, _: number) => { @@ -25,7 +25,7 @@ export const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string }, retryOptions); }; -export const setupDecoratorLambdaHandler = (metrics: Metrics, options: ExtraOptions = {}): Handler => { +const setupDecoratorLambdaHandler = (metrics: Metrics, options: ExtraOptions = {}): Handler => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics(options) @@ -44,3 +44,8 @@ export const setupDecoratorLambdaHandler = (metrics: Metrics, options: ExtraOpti return handler; }; +export { + getMetrics, + setupDecoratorLambdaHandler +}; + From e71c1feca814d591ba8e24cc41930ef10c80bfa0 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 12:39:46 +0600 Subject: [PATCH 87/95] refactor: LooseObject interface in metrics tests --- packages/metrics/tests/unit/Metrics.test.ts | 22 ++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 739aed3183..79403ac56c 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -17,6 +17,10 @@ import { setupDecoratorLambdaHandler } from '../helpers/metricsUtils'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); +interface LooseObject { + [key: string]: string +} + describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; @@ -90,7 +94,7 @@ describe('Class: Metrics', () => { test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { // Prepare - const defaultDimensions : { [key: string]: string } = { 'environment': 'dev', 'foo': 'bar' }; + const defaultDimensions : LooseObject = { 'environment': 'dev', 'foo': 'bar' }; const metrics: Metrics = createMetrics({ namespace:'test', defaultDimensions }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -111,7 +115,7 @@ describe('Class: Metrics', () => { test('it should add multiple dimensions', () => { // Prepare - const dimensionsToBeAdded: { [key: string]: string } = { + const dimensionsToBeAdded: LooseObject = { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; @@ -130,7 +134,7 @@ describe('Class: Metrics', () => { test('it should update existing dimension value if same dimension is added again', () => { // Prepare - const dimensionsToBeAdded: { [key: string]: string } = { + const dimensionsToBeAdded: LooseObject = { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; @@ -156,7 +160,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - const dimensionsToBeAdded: { [key: string]: string } = {}; + const dimensionsToBeAdded: LooseObject = {}; for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } @@ -174,7 +178,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - const dimensionsToBeAdded: { [key: string]: string } = {}; + const dimensionsToBeAdded: LooseObject = {}; for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } @@ -1154,7 +1158,7 @@ describe('Class: Metrics', () => { // Prepare const testMetric = 'test-metric'; - const additionalDimensions: { [key: string]: string } = { + const additionalDimensions: LooseObject = { metric2: 'metric2Value', metric3: 'metric3Value' }; @@ -1658,7 +1662,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - const defaultDimensions: { [key: string]: string } = {}; + const defaultDimensions: LooseObject = {}; for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } @@ -1673,7 +1677,7 @@ describe('Class: Metrics', () => { test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { // Prepare - const initialDefaultDimensions: { [key: string]: string } = { + const initialDefaultDimensions: LooseObject = { 'test-dimension': 'test-value', 'environment': 'dev' }; @@ -1683,7 +1687,7 @@ describe('Class: Metrics', () => { }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - const defaultDimensions: { [key: string]: string } = {}; + const defaultDimensions: LooseObject = {}; for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(initialDefaultDimensions).length); i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } From 4a9dbc3f1e0568f65ce96c2419ba1f059f6d6dd5 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 14:12:27 +0600 Subject: [PATCH 88/95] refactor: range error tests for max dimension count --- packages/metrics/tests/unit/Metrics.test.ts | 91 +++++++++++++-------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 79403ac56c..2053faa778 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -82,29 +82,42 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - // Act & Assess - expect(() => { - for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + // Act + // Starts from 1 because the service dimension is already added by default + for (let i = 1; i < MAX_DIMENSION_COUNT; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + + // Assess + expect(Object.keys(metrics['defaultDimensions']).length).toBe(1); + expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); + expect(() => metrics.addDimension('another-dimension', 'another-dimension-value')) + .toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); test('it should take consideration of defaultDimensions while throwing error if number of dimensions exceeds the maximum allowed', () => { // Prepare - const defaultDimensions : LooseObject = { 'environment': 'dev', 'foo': 'bar' }; - const metrics: Metrics = createMetrics({ namespace:'test', defaultDimensions }); + const defaultDimensions: LooseObject = { + 'environment': 'dev', + 'foo': 'bar' + }; + const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, defaultDimensions }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - // Act & Assess - expect(() => { - for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(defaultDimensions).length); i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - }).toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + // Act + // Starts from 3 because three default dimensions are already set (service, environment, foo) + for (let i = 3; i < MAX_DIMENSION_COUNT; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + + // Assess + expect(Object.keys(metrics['defaultDimensions']).length).toBe(3); + expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT - 3); + expect(() => metrics.addDimension('another-dimension', 'another-dimension-value')) + .toThrowError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); @@ -154,25 +167,24 @@ describe('Class: Metrics', () => { }); - test('it should throw error if number of dimensions exceeds the maximum allowed', () => { + test('it should successfully add up to maximum allowed dimensions without throwing error', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: LooseObject = {}; - for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { + for (let i = 0; i < MAX_DIMENSION_COUNT; i++) { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } // Act & Assess - expect(() => { - metrics.addDimensions(dimensionsToBeAdded); - }).toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + expect(() => { metrics.addDimensions(dimensionsToBeAdded);}).not.toThrowError(); + expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT); }); - test('it should successfully add up to maximum allowed dimensions without throwing error', () => { + test('it should throw error if number of dimensions exceeds the maximum allowed', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); @@ -183,11 +195,14 @@ describe('Class: Metrics', () => { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } - // Act & Assess + // Act + metrics.addDimensions(dimensionsToBeAdded); + + // Assess + expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT); expect(() => { - metrics.addDimensions(dimensionsToBeAdded); - }).not.toThrowError(`Unable to add ${Object.keys(dimensionsToBeAdded).length} dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); - expect(metrics).toEqual(expect.objectContaining({ dimensions: dimensionsToBeAdded })); + metrics.addDimensions({ 'another-dimension': 'another-dimension-value' }); + }).toThrowError(`Unable to add 1 dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); @@ -1656,25 +1671,31 @@ describe('Class: Metrics', () => { }); - test('it should throw error if number of dimensions reaches the maximum allowed', () => { + test('it should throw error if number of default dimensions reaches the maximum allowed', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: LooseObject = {}; - for (let i = 0; i <= MAX_DIMENSION_COUNT; i++) { + + // Starts from 1 because the service dimension is already added by default + for (let i = 1; i < MAX_DIMENSION_COUNT - 1; i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } + + // Act + metrics.setDefaultDimensions(defaultDimensions); - // Act & Assess + // Assess + expect(Object.keys(metrics['defaultDimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); expect(() => { - metrics.setDefaultDimensions(defaultDimensions); + metrics.setDefaultDimensions({ 'another-dimension': 'another-dimension-value' }); }).toThrowError('Max dimension count hit'); }); - test('it should consider default dimensions provided in constructor, while throwing error if number of dimensions exceeds the maximum allowed', () => { + test('it should consider default dimensions provided in constructor, while throwing error if number of default dimensions reaches the maximum allowed', () => { // Prepare const initialDefaultDimensions: LooseObject = { @@ -1688,13 +1709,19 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: LooseObject = {}; - for (let i = 0; i < (MAX_DIMENSION_COUNT - Object.keys(initialDefaultDimensions).length); i++) { + + // Starts from 3 because the service dimension is already added by default & two dimensions are already added in the constructor + for (let i = 3; i < MAX_DIMENSION_COUNT - 1; i++) { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } + + // Act + metrics.setDefaultDimensions(defaultDimensions); - // Act & Assess + // Assess + expect(Object.keys(metrics['defaultDimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); expect(() => { - metrics.setDefaultDimensions(defaultDimensions); + metrics.setDefaultDimensions({ 'another-dimension': 'another-dimension-value' }); }).toThrowError('Max dimension count hit'); }); From 3ef1a7bc9c7e45da61ab68b784eae99931c447da Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 14:41:08 +0600 Subject: [PATCH 89/95] refactor: test range error for max dimension in addDimension --- packages/metrics/tests/unit/Metrics.test.ts | 42 +++++++++------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 2053faa778..6287645fca 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -82,13 +82,13 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - // Act + // Act & Assess // Starts from 1 because the service dimension is already added by default - for (let i = 1; i < MAX_DIMENSION_COUNT; i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - - // Assess + expect(() => { + for (let i = 1; i < MAX_DIMENSION_COUNT; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).not.toThrowError(); expect(Object.keys(metrics['defaultDimensions']).length).toBe(1); expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); expect(() => metrics.addDimension('another-dimension', 'another-dimension-value')) @@ -107,13 +107,13 @@ describe('Class: Metrics', () => { const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; - // Act + // Act & Assess // Starts from 3 because three default dimensions are already set (service, environment, foo) - for (let i = 3; i < MAX_DIMENSION_COUNT; i++) { - metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); - } - - // Assess + expect(() => { + for (let i = 3; i < MAX_DIMENSION_COUNT; i++) { + metrics.addDimension(`${dimensionName}-${i}`, `${dimensionValue}-${i}`); + } + }).not.toThrowError(); expect(Object.keys(metrics['defaultDimensions']).length).toBe(3); expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT - 3); expect(() => metrics.addDimension('another-dimension', 'another-dimension-value')) @@ -195,10 +195,8 @@ describe('Class: Metrics', () => { dimensionsToBeAdded[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } - // Act - metrics.addDimensions(dimensionsToBeAdded); - - // Assess + // Act & Assess + expect(() => metrics.addDimensions(dimensionsToBeAdded)).not.toThrowError(); expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT); expect(() => { metrics.addDimensions({ 'another-dimension': 'another-dimension-value' }); @@ -1684,10 +1682,8 @@ describe('Class: Metrics', () => { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } - // Act - metrics.setDefaultDimensions(defaultDimensions); - - // Assess + // Act & Assess + expect(() => metrics.setDefaultDimensions(defaultDimensions)).not.toThrowError(); expect(Object.keys(metrics['defaultDimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); expect(() => { metrics.setDefaultDimensions({ 'another-dimension': 'another-dimension-value' }); @@ -1715,10 +1711,8 @@ describe('Class: Metrics', () => { defaultDimensions[`${dimensionName}-${i}`] = `${dimensionValue}-${i}`; } - // Act - metrics.setDefaultDimensions(defaultDimensions); - - // Assess + // Act & Assess + expect(() => metrics.setDefaultDimensions(defaultDimensions)).not.toThrowError(); expect(Object.keys(metrics['defaultDimensions']).length).toBe(MAX_DIMENSION_COUNT - 1); expect(() => { metrics.setDefaultDimensions({ 'another-dimension': 'another-dimension-value' }); From 73a69cb24ab1f00c43926eb69e6d3fcc6ca043a0 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 14:41:50 +0600 Subject: [PATCH 90/95] refactor: range error tests for max metric size --- packages/metrics/tests/unit/Metrics.test.ts | 41 ++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 6287645fca..505f92a7c5 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -370,20 +370,32 @@ describe('Class: Metrics', () => { }); - test('it should publish metrics if stored metrics count has reached max metric size threshold', () => { + test('it should publish metrics if stored metrics count has already reached max metric size threshold & then store remaining metric', () => { // Prepare const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; - // Act - for (let i = 0; i <= MAX_METRICS_SIZE; i++) { - metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); - } - - // Assess + // Act & Assess + expect(() => { + for (let i = 0; i < MAX_METRICS_SIZE; i++) { + metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + } + }).not.toThrowError(); + expect(Object.keys(metrics['storedMetrics']).length).toEqual(MAX_METRICS_SIZE); + metrics.addMetric('another-metric', MetricUnits.Count, MAX_METRICS_SIZE + 1); expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(1); + expect(metrics).toEqual(expect.objectContaining({ + storedMetrics: { + 'another-metric': { + name: 'another-metric', + resolution: MetricResolution.Standard, + unit: MetricUnits.Count, + value: MAX_METRICS_SIZE + 1 + } + }, + })); }); @@ -394,13 +406,16 @@ describe('Class: Metrics', () => { const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; - // Act - for (let i = 0; i < MAX_METRICS_SIZE; i++) { - metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); - } - - // Assess + // Act & Assess + expect(() => { + for (let i = 0; i < MAX_METRICS_SIZE - 1; i++) { + metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + } + }).not.toThrowError(); + expect(Object.keys(metrics['storedMetrics']).length).toEqual(MAX_METRICS_SIZE - 1); + metrics.addMetric('another-metric', MetricUnits.Count, MAX_METRICS_SIZE); expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); + expect(Object.keys(metrics['storedMetrics']).length).toEqual(MAX_METRICS_SIZE); }); From ffd14d2b01c90c33cadc4d0996ad0407977e9959 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 14:45:39 +0600 Subject: [PATCH 91/95] refactor: test namespace value from constant --- packages/metrics/tests/unit/Metrics.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 505f92a7c5..f569b3ae05 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -960,7 +960,7 @@ describe('Class: Metrics', () => { // Prepare const metrics: Metrics = createMetrics({ - namespace: 'test-namespace', + namespace: TEST_NAMESPACE, serviceName: 'test-service', defaultDimensions: { 'environment': 'dev' @@ -980,7 +980,7 @@ describe('Class: Metrics', () => { 'Timestamp': mockDate.getTime(), 'CloudWatchMetrics': [ { - 'Namespace': 'test-namespace', + 'Namespace': TEST_NAMESPACE, 'Dimensions': [ [ 'service', From e6d11e20cd9d81c6f6c35636f4d97df79c6f7887 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Thu, 20 Apr 2023 14:54:07 +0600 Subject: [PATCH 92/95] style: redundant curly braces --- packages/metrics/tests/unit/Metrics.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index f569b3ae05..3271ae6aa5 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -179,7 +179,7 @@ describe('Class: Metrics', () => { } // Act & Assess - expect(() => { metrics.addDimensions(dimensionsToBeAdded);}).not.toThrowError(); + expect(() => metrics.addDimensions(dimensionsToBeAdded)).not.toThrowError(); expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT); }); @@ -198,9 +198,9 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => metrics.addDimensions(dimensionsToBeAdded)).not.toThrowError(); expect(Object.keys(metrics['dimensions']).length).toBe(MAX_DIMENSION_COUNT); - expect(() => { - metrics.addDimensions({ 'another-dimension': 'another-dimension-value' }); - }).toThrowError(`Unable to add 1 dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); + expect(() => + metrics.addDimensions({ 'another-dimension': 'another-dimension-value' }) + ).toThrowError(`Unable to add 1 dimensions: the number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`); }); From af2a6b8388a4f2284e5549b04dfe9b63c8d48f1a Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Fri, 21 Apr 2023 14:19:18 +0600 Subject: [PATCH 93/95] refactor: replace createMetrics helper with new Metrics(...). This helper will be removed/deprecated from all utilities(i.e. createLogger) in the next major release. So, avoiding it is logical. --- packages/metrics/tests/unit/Metrics.test.ts | 138 ++++++++++---------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 3271ae6aa5..d365c6140a 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -8,7 +8,7 @@ import { ContextExamples as dummyContext, Events as dummyEvent } from '@aws-lambda-powertools/commons'; -import { MetricResolution, MetricUnits, Metrics, createMetrics } from '../../src/'; +import { MetricResolution, MetricUnits, Metrics } from '../../src/'; import { Context, Handler } from 'aws-lambda'; import { Dimensions, EmfOutput } from '../../src/types'; import { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; @@ -40,7 +40,7 @@ describe('Class: Metrics', () => { test('when called, it should store dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue= 'test-value'; @@ -59,7 +59,7 @@ describe('Class: Metrics', () => { test('it should update existing dimension value if same dimension is added again', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; // Act @@ -78,7 +78,7 @@ describe('Class: Metrics', () => { test('it should throw error if the number of dimensions exceeds the maximum allowed', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -103,7 +103,7 @@ describe('Class: Metrics', () => { 'environment': 'dev', 'foo': 'bar' }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, defaultDimensions }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, defaultDimensions }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; @@ -132,7 +132,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addDimensions(dimensionsToBeAdded); @@ -151,7 +151,7 @@ describe('Class: Metrics', () => { 'test-dimension-1': 'test-value-1', 'test-dimension-2': 'test-value-2', }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addDimensions(dimensionsToBeAdded); @@ -170,7 +170,7 @@ describe('Class: Metrics', () => { test('it should successfully add up to maximum allowed dimensions without throwing error', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: LooseObject = {}; @@ -187,7 +187,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of dimensions exceeds the maximum allowed', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const dimensionsToBeAdded: LooseObject = {}; @@ -211,7 +211,7 @@ describe('Class: Metrics', () => { test('it should add metadata', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetadata('foo', 'bar'); @@ -226,7 +226,7 @@ describe('Class: Metrics', () => { test('it should update existing metadata value if same metadata is added again', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetadata('foo', 'bar'); @@ -245,7 +245,7 @@ describe('Class: Metrics', () => { test('it should store metrics when called', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; // Act @@ -268,7 +268,7 @@ describe('Class: Metrics', () => { test('it should store multiple metrics when called with multiple metric name', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric('test-metric-1', MetricUnits.Count, 1, MetricResolution.High); @@ -304,7 +304,7 @@ describe('Class: Metrics', () => { test('it should store metrics with standard resolution when called without resolution', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric('test-metric-1', MetricUnits.Count, 1); @@ -333,7 +333,7 @@ describe('Class: Metrics', () => { test('it should group the metric values together in an array when trying to add same metric with different values', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; // Act @@ -359,7 +359,7 @@ describe('Class: Metrics', () => { test('it should throw an error when trying to add same metric with different unit', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; // Act & Assess @@ -373,7 +373,7 @@ describe('Class: Metrics', () => { test('it should publish metrics if stored metrics count has already reached max metric size threshold & then store remaining metric', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -402,7 +402,7 @@ describe('Class: Metrics', () => { test('it should not publish metrics if stored metrics count has not reached max metric size threshold', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); const metricName = 'test-metric'; @@ -422,7 +422,7 @@ describe('Class: Metrics', () => { test('it should publish metrics on every call if singleMetric is set to true', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); // Act @@ -437,7 +437,7 @@ describe('Class: Metrics', () => { test('it should not publish metrics on every call if singleMetric is set to false', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: false }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: false }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); // Act @@ -452,7 +452,7 @@ describe('Class: Metrics', () => { test('it should not publish metrics on every call if singleMetric is not provided', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); // Act @@ -471,8 +471,8 @@ describe('Class: Metrics', () => { test('it should call addMetric with correct parameters', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); @@ -493,8 +493,8 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE, defaultDimensions }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, defaultDimensions }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -511,8 +511,8 @@ describe('Class: Metrics', () => { test('it should call setDefaultDimensions with correct parameters when defaultDimensions are not set', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -530,9 +530,9 @@ describe('Class: Metrics', () => { // Prepare const functionName = 'coldStart'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.setFunctionName(functionName); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); @@ -549,8 +549,8 @@ describe('Class: Metrics', () => { test('it should not call addDimension, if functionName is not set', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addDimensionSpy = jest.spyOn(singleMetricMock, 'addDimension'); @@ -566,10 +566,10 @@ describe('Class: Metrics', () => { test('it should not call any function, if there is no cold start', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); jest.spyOn(metrics, 'isColdStart').mockImplementation(() => false); - const singleMetricMock: Metrics = createMetrics({ namespace: TEST_NAMESPACE, singleMetric: true }); + const singleMetricMock: Metrics = new Metrics({ namespace: TEST_NAMESPACE, singleMetric: true }); const singleMetricSpy = jest.spyOn(metrics, 'singleMetric').mockImplementation(() => singleMetricMock); const addMetricSpy = jest.spyOn(singleMetricMock, 'addMetric'); const setDefaultDimensionsSpy = jest.spyOn(singleMetricMock, 'setDefaultDimensions'); @@ -593,7 +593,7 @@ describe('Class: Metrics', () => { test('it should clear all default dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.setDefaultDimensions({ 'foo': 'bar' }); // Act @@ -609,7 +609,7 @@ describe('Class: Metrics', () => { test('it should only clear default dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.setDefaultDimensions({ 'foo': 'bar' }); metrics.addDimension('environment', 'dev'); @@ -633,7 +633,7 @@ describe('Class: Metrics', () => { test('it should clear all dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addDimension('foo', 'bar'); // Act @@ -649,7 +649,7 @@ describe('Class: Metrics', () => { test('it should only clear dimensions', () => { // Prepare - const metrics: Metrics = createMetrics({ defaultDimensions: { 'environment': 'dev' } }); + const metrics: Metrics = new Metrics({ defaultDimensions: { 'environment': 'dev' } }); metrics.addDimension('foo', 'bar'); // Act @@ -673,7 +673,7 @@ describe('Class: Metrics', () => { test('it should clear all metadata', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addMetadata('foo', 'bar'); metrics.addMetadata('test', 'baz'); @@ -694,7 +694,7 @@ describe('Class: Metrics', () => { test('it should clear stored metrics', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const metricName = 'test-metric'; // Act @@ -722,7 +722,7 @@ describe('Class: Metrics', () => { const decoratorLambdaMetric= 'decorator-lambda-test-metric'; beforeEach(() => { - metrics = createMetrics({ namespace: TEST_NAMESPACE }); + metrics = new Metrics({ namespace: TEST_NAMESPACE }); publishStoredMetricsSpy = jest.spyOn(metrics, 'publishStoredMetrics'); addMetricSpy = jest.spyOn(metrics, 'addMetric'); captureColdStartMetricSpy = jest.spyOn(metrics, 'captureColdStartMetric'); @@ -835,7 +835,7 @@ describe('Class: Metrics', () => { test('it should log warning if no metrics are added & throwOnEmptyMetrics is false', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); // Act @@ -852,7 +852,7 @@ describe('Class: Metrics', () => { test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metric', MetricUnits.Count, 10); const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); const mockData: EmfOutput = { @@ -893,7 +893,7 @@ describe('Class: Metrics', () => { test('it should call clearMetrics function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); @@ -908,7 +908,7 @@ describe('Class: Metrics', () => { test('it should call clearDimensions function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); @@ -923,7 +923,7 @@ describe('Class: Metrics', () => { test('it should call clearMetadata function', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); metrics.addMetric('test-metric', MetricUnits.Count, 10); const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); @@ -959,7 +959,7 @@ describe('Class: Metrics', () => { test('it should return right object compliant with Cloudwatch EMF', () => { // Prepare - const metrics: Metrics = createMetrics({ + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, serviceName: 'test-service', defaultDimensions: { @@ -1014,7 +1014,7 @@ describe('Class: Metrics', () => { // Prepare const serviceName = 'test-service'; const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics({ serviceName: serviceName, namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ serviceName: serviceName, namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1054,7 +1054,7 @@ describe('Class: Metrics', () => { const serviceName = 'hello-world-service'; process.env.POWERTOOLS_SERVICE_NAME = serviceName; const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1096,7 +1096,7 @@ describe('Class: Metrics', () => { 'env': 'dev' }; const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics({ defaultDimensions: additionalDimensions, namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ defaultDimensions: additionalDimensions, namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1142,7 +1142,7 @@ describe('Class: Metrics', () => { // Prepare const testMetric = 'test-metric'; const additionalDimension = { name: 'metric2', value: 'metric2Value' }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric('test-metric', MetricUnits.Count, 10, MetricResolution.High); @@ -1190,7 +1190,7 @@ describe('Class: Metrics', () => { metric2: 'metric2Value', metric3: 'metric3Value' }; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10, MetricResolution.High); @@ -1238,7 +1238,7 @@ describe('Class: Metrics', () => { // Prepare const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1277,7 +1277,7 @@ describe('Class: Metrics', () => { test('it should throw error on empty metrics when throwOnEmptyMetrics is true', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.throwOnEmptyMetrics(); @@ -1292,7 +1292,7 @@ describe('Class: Metrics', () => { // Prepare process.env.POWERTOOLS_METRICS_NAMESPACE = ''; const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics(); + const metrics: Metrics = new Metrics(); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1330,7 +1330,7 @@ describe('Class: Metrics', () => { // Prepare const testMetric = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(testMetric, MetricUnits.Count, 10); @@ -1368,7 +1368,7 @@ describe('Class: Metrics', () => { // Prepare const metricName = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1407,7 +1407,7 @@ describe('Class: Metrics', () => { // Prepare const metricName = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1448,7 +1448,7 @@ describe('Class: Metrics', () => { // Prepare const metricName1 = 'test-metric-1'; const metricName2 = 'test-metric-2'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(metricName1, MetricUnits.Count, 10); @@ -1494,7 +1494,7 @@ describe('Class: Metrics', () => { // Prepare const metricName = 'test-metric'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(metricName, MetricUnits.Count, 10); @@ -1534,7 +1534,7 @@ describe('Class: Metrics', () => { // Prepare const metricName1 = 'test-metric'; const metricName2 = 'test-metric-2'; - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.addMetric(metricName1, MetricUnits.Count, 10); @@ -1585,7 +1585,7 @@ describe('Class: Metrics', () => { // Prepare const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ serviceName: serviceName }); + const metrics: Metrics = new Metrics({ serviceName: serviceName }); const defaultDimensionsToBeAdded = { 'environment': 'dev', 'foo': 'bar', @@ -1607,7 +1607,7 @@ describe('Class: Metrics', () => { test('it should set default dimensions correctly when service name is not provided', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const defaultDimensionsToBeAdded = { 'environment': 'dev', 'foo': 'bar', @@ -1630,7 +1630,7 @@ describe('Class: Metrics', () => { // Prepare const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, serviceName, defaultDimensions: { 'test-dimension': 'test-dimension-value' } @@ -1658,7 +1658,7 @@ describe('Class: Metrics', () => { // Prepare const serviceName = 'test-service'; - const metrics: Metrics = createMetrics({ + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, serviceName, defaultDimensions: { @@ -1687,7 +1687,7 @@ describe('Class: Metrics', () => { test('it should throw error if number of default dimensions reaches the maximum allowed', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); const dimensionName = 'test-dimension'; const dimensionValue = 'test-value'; const defaultDimensions: LooseObject = {}; @@ -1713,7 +1713,7 @@ describe('Class: Metrics', () => { 'test-dimension': 'test-value', 'environment': 'dev' }; - const metrics: Metrics = createMetrics({ + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, defaultDimensions: initialDefaultDimensions }); @@ -1742,7 +1742,7 @@ describe('Class: Metrics', () => { test('it should set the function name', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.setFunctionName('test-function'); @@ -1765,7 +1765,7 @@ describe('Class: Metrics', () => { 'foo': 'bar', 'service': 'order' }; - const metrics: Metrics = createMetrics({ + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE, defaultDimensions, singleMetric: false @@ -1790,7 +1790,7 @@ describe('Class: Metrics', () => { test('it should set the throwOnEmptyMetrics flag to true', () => { // Prepare - const metrics: Metrics = createMetrics({ namespace: TEST_NAMESPACE }); + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act metrics.throwOnEmptyMetrics(); From a063546d37bf58013574705a6ec701cdc387dc23 Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Fri, 21 Apr 2023 15:05:35 +0600 Subject: [PATCH 94/95] test: constructor method of Metrics class. Removing createMetrics helper function and related tests impacts 100% code coverage. So, instead createMetrics tests can be used as constructor method tests for Metrics class. As a result 100% test coverage will not be impacted. --- packages/metrics/tests/unit/Metrics.test.ts | 250 +++++++++++++++++++- 1 file changed, 249 insertions(+), 1 deletion(-) diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index d365c6140a..9fcd932efb 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -10,9 +10,10 @@ import { } from '@aws-lambda-powertools/commons'; import { MetricResolution, MetricUnits, Metrics } from '../../src/'; import { Context, Handler } from 'aws-lambda'; -import { Dimensions, EmfOutput } from '../../src/types'; +import { Dimensions, EmfOutput, MetricsOptions } from '../../src/types'; import { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE } from '../../src/constants'; import { setupDecoratorLambdaHandler } from '../helpers/metricsUtils'; +import { ConfigServiceInterface, EnvironmentVariablesService } from '../../src/config'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -35,6 +36,253 @@ describe('Class: Metrics', () => { process.env = { ...ENVIRONMENT_VARIABLES }; }); + describe('Method: constructor', () => { + + test('when no constructor parameters are set, creates instance with the options set in the environment variables', () => { + + // Prepare + const metricsOptions = undefined; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when no constructor parameters and no environment variables are set, creates instance with the default properties', () => { + + // Prepare + const metricsOptions = undefined; + process.env = {}; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: '', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when constructor parameters are set, creates instance with the options set in the constructor parameters', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + customConfigService: new EnvironmentVariablesService(), + namespace: TEST_NAMESPACE, + serviceName: 'test-service', + singleMetric: true, + defaultDimensions: { + service: 'order', + }, + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: expect.any(EnvironmentVariablesService), + defaultDimensions: metricsOptions.defaultDimensions, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: true, + metadata: {}, + namespace: metricsOptions.namespace, + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + }); + + test('when custom namespace is passed, creates instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + namespace: TEST_NAMESPACE, + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: metricsOptions.namespace, + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom defaultDimensions is passed, creates instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + defaultDimensions: { + service: 'order', + }, + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: metricsOptions.defaultDimensions, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when singleMetric is passed, creates instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + singleMetric: true, + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'service_undefined', + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: true, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom customConfigService is passed, creates instance with the correct properties', () => { + + // Prepare + const configService: ConfigServiceInterface = { + get(name: string): string { + return `a-string-from-${name}`; + }, + getNamespace(): string{ + return TEST_NAMESPACE; + }, + getServiceName(): string{ + return 'test-service'; + } + }; + const metricsOptions: MetricsOptions = { + customConfigService: configService, + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: configService, + defaultDimensions: { + service: 'test-service' + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: TEST_NAMESPACE, + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + test('when custom serviceName is passed, creates instance with the correct properties', () => { + + // Prepare + const metricsOptions: MetricsOptions = { + serviceName: 'test-service', + }; + + // Act + const metrics: Metrics = new Metrics(metricsOptions); + + // Assess + expect(metrics).toEqual(expect.objectContaining({ + coldStart: true, + customConfigService: undefined, + defaultDimensions: { + service: 'test-service' + }, + defaultServiceName: 'service_undefined', + dimensions: {}, + envVarsService: expect.any(EnvironmentVariablesService), + isSingleMetric: false, + metadata: {}, + namespace: 'hello-world', + shouldThrowOnEmptyMetrics: false, + storedMetrics: {} + })); + + }); + + }); + describe('Method: addDimension', () => { test('when called, it should store dimensions', () => { From 4037c3c10d9793c76b867e6d9bcf0091b40b031e Mon Sep 17 00:00:00 2001 From: Arnab Rahman Date: Fri, 21 Apr 2023 15:07:59 +0600 Subject: [PATCH 95/95] refactor: remove createMetrics helper function related codes --- packages/metrics/src/helpers.ts | 8 - packages/metrics/src/index.ts | 1 - packages/metrics/tests/unit/helpers.test.ts | 279 -------------------- 3 files changed, 288 deletions(-) delete mode 100644 packages/metrics/src/helpers.ts delete mode 100644 packages/metrics/tests/unit/helpers.test.ts diff --git a/packages/metrics/src/helpers.ts b/packages/metrics/src/helpers.ts deleted file mode 100644 index 16a9c7f2a8..0000000000 --- a/packages/metrics/src/helpers.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MetricsOptions } from 'types'; -import { Metrics } from '.'; - -const createMetrics = (options: MetricsOptions = {}): Metrics => new Metrics(options); - -export { - createMetrics, -}; \ No newline at end of file diff --git a/packages/metrics/src/index.ts b/packages/metrics/src/index.ts index 8002a99314..ffa141c1e6 100644 --- a/packages/metrics/src/index.ts +++ b/packages/metrics/src/index.ts @@ -1,4 +1,3 @@ -export * from './helpers'; export * from './Metrics'; export * from './MetricsInterface'; export * from './middleware'; \ No newline at end of file diff --git a/packages/metrics/tests/unit/helpers.test.ts b/packages/metrics/tests/unit/helpers.test.ts deleted file mode 100644 index 8610828d7d..0000000000 --- a/packages/metrics/tests/unit/helpers.test.ts +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Test Metrics helpers - * - * @group unit/metrics/all - */ -import { createMetrics, Metrics } from '../../src'; -import { ConfigServiceInterface, EnvironmentVariablesService } from '../../src/config'; -import { MetricsOptions } from '../../src/types'; - -describe('Helper: createMetrics function', () => { - - const ENVIRONMENT_VARIABLES = process.env; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...ENVIRONMENT_VARIABLES }; - }); - - afterAll(() => { - process.env = ENVIRONMENT_VARIABLES; - }); - - describe('MetricsOptions constructor parameters', () => { - - test('when no constructor parameters are set, returns a Metrics instance with the options set in the environment variables', () => { - - // Prepare - const metricsOptions = undefined; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: { - service: 'service_undefined', - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: 'hello-world', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when no constructor parameters and no environment variables are set, returns a Metrics instance with the default properties', () => { - - // Prepare - const metricsOptions = undefined; - process.env = {}; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: { - service: 'service_undefined', - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: '', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when constructor parameters are set, returns a Metrics instance with the options set in the constructor parameters', () => { - - // Prepare - const metricsOptions: MetricsOptions = { - customConfigService: new EnvironmentVariablesService(), - namespace: 'test-namespace', - serviceName: 'test-service', - singleMetric: true, - defaultDimensions: { - service: 'order', - }, - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: expect.any(EnvironmentVariablesService), - defaultDimensions: metricsOptions.defaultDimensions, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: true, - metadata: {}, - namespace: metricsOptions.namespace, - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - }); - - test('when custom namespace is passed, returns a Metrics instance with the correct properties', () => { - - // Prepare - const metricsOptions: MetricsOptions = { - namespace: 'test-namespace', - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: { - service: 'service_undefined', - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: metricsOptions.namespace, - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when custom defaultDimensions is passed, returns a Metrics instance with the correct properties', () => { - - // Prepare - const metricsOptions: MetricsOptions = { - defaultDimensions: { - service: 'order', - }, - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: metricsOptions.defaultDimensions, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: 'hello-world', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when singleMetric is passed, returns a Metrics instance with the correct properties', () => { - - // Prepare - const metricsOptions: MetricsOptions = { - singleMetric: true, - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: { - service: 'service_undefined', - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: true, - metadata: {}, - namespace: 'hello-world', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when custom customConfigService is passed, returns a Metrics instance with the correct properties', () => { - - // Prepare - const configService: ConfigServiceInterface = { - get(name: string): string { - return `a-string-from-${name}`; - }, - getNamespace(): string{ - return 'test-namespace'; - }, - getServiceName(): string{ - return 'test-service'; - } - }; - const metricsOptions: MetricsOptions = { - customConfigService: configService, - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: configService, - defaultDimensions: { - service: 'test-service' - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: 'test-namespace', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - test('when custom serviceName is passed, returns a Metrics instance with the correct properties', () => { - - // Prepare - const metricsOptions: MetricsOptions = { - serviceName: 'test-service', - }; - - // Act - const metrics = createMetrics(metricsOptions); - - // Assess - expect(metrics).toBeInstanceOf(Metrics); - expect(metrics).toEqual(expect.objectContaining({ - coldStart: true, - customConfigService: undefined, - defaultDimensions: { - service: 'test-service' - }, - defaultServiceName: 'service_undefined', - dimensions: {}, - envVarsService: expect.any(EnvironmentVariablesService), - isSingleMetric: false, - metadata: {}, - namespace: 'hello-world', - shouldThrowOnEmptyMetrics: false, - storedMetrics: {} - })); - - }); - - }); - -}); \ No newline at end of file