From 2d4f4eb01f577b9e64418ea2f3f89da51a99b22a Mon Sep 17 00:00:00 2001 From: manushak Date: Tue, 2 Apr 2024 12:29:06 +0400 Subject: [PATCH 1/2] feat(lib): make small code imporovments --- src/lib/ccf/index.ts | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/lib/ccf/index.ts b/src/lib/ccf/index.ts index 385651f..0e5b3a0 100644 --- a/src/lib/ccf/index.ts +++ b/src/lib/ccf/index.ts @@ -271,36 +271,27 @@ export const CloudCarbonFootprint = ( * Validates the resolved architecture using the validateAwsArchitecture method. */ const resolveAwsArchitecture = (architecture: string) => { - const modifyArchitecture: {[key: string]: () => void} = { - 'AMD ': () => { - architecture = architecture.substring(4); - }, - Skylake: () => { - architecture = 'Sky Lake'; - }, - Graviton: () => { - architecture = architecture.includes('2') ? 'Graviton2' : 'Graviton'; - }, - Unknown: () => { - architecture = 'Average'; - }, + let formattedArchitecture = architecture; + + const architectureFormat: {[key: string]: () => string} = { + 'AMD ': () => architecture.substring(4), + Skylake: () => 'Sky Lake', + Graviton: () => (architecture.includes('2') ? 'Graviton2' : 'Graviton'), + Unknown: () => 'Average', }; - Object.keys(modifyArchitecture).forEach(key => { + Object.keys(architectureFormat).forEach(key => { if (architecture.includes(key)) { - modifyArchitecture[key](); + formattedArchitecture = architectureFormat[key](); } }); - validateAwsArchitecture(architecture); - - return architecture; + return formattedArchitecture; }; /** * Validates the AWS instance architecture against a predefined set of supported architectures. */ - const validateAwsArchitecture = (architecture: string) => { if (!(architecture in instanceUsage['aws'])) { throw new UnsupportedValueError( @@ -381,9 +372,12 @@ export const CloudCarbonFootprint = ( instance['Instance type'] ] ?? ['Average']; - return architectures.map((architecture: string) => - resolveAwsArchitecture(architecture) - ); + return architectures.map((architecture: string) => { + const AWSArchitecture = resolveAwsArchitecture(architecture); + validateAwsArchitecture(AWSArchitecture); + + return AWSArchitecture; + }); }; /** From 7532934b33afeaa1729c4f58527fd823227587ff Mon Sep 17 00:00:00 2001 From: manushak Date: Tue, 2 Apr 2024 12:30:42 +0400 Subject: [PATCH 2/2] test(lib): add full test coverage for ccf - seperate test file to mock aws-use.json data --- src/__tests__/unit/lib/ccf/ccf-main.test.ts | 361 +++++++++++++++++++ src/__tests__/unit/lib/ccf/ccf-usage.test.ts | 39 ++ src/__tests__/unit/lib/ccf/index.test.ts | 339 +---------------- 3 files changed, 402 insertions(+), 337 deletions(-) create mode 100644 src/__tests__/unit/lib/ccf/ccf-main.test.ts create mode 100644 src/__tests__/unit/lib/ccf/ccf-usage.test.ts diff --git a/src/__tests__/unit/lib/ccf/ccf-main.test.ts b/src/__tests__/unit/lib/ccf/ccf-main.test.ts new file mode 100644 index 0000000..610aa0f --- /dev/null +++ b/src/__tests__/unit/lib/ccf/ccf-main.test.ts @@ -0,0 +1,361 @@ +import {CloudCarbonFootprint} from '../../../../lib/ccf/index'; + +import {ERRORS} from '../../../../util/errors'; +const {InputValidationError, UnsupportedValueError} = ERRORS; + +describe('lib/ccf: ', () => { + describe('CloudCarbonFootprint: ', () => { + const output = CloudCarbonFootprint(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('init CloudCarbonFootprint: ', () => { + it('initalizes object with properties.', async () => { + expect(output).toHaveProperty('metadata'); + expect(output).toHaveProperty('execute'); + }); + }); + + describe('execute(): ', () => { + describe('init with `aws` value of `cloud/vendor`.', () => { + it('executes with valid data, without `interpolation` config.', async () => { + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't2.micro', + }, + ]; + + const result = await output.execute(inputs); + + expect.assertions(1); + expect(result).toStrictEqual([ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't2.micro', + 'carbon-embodied': 0.8784841133942161, + energy: 0.0023031270462730543, + }, + ]); + }); + + it('executes with valid data, with `interpolation` config.', async () => { + const output = CloudCarbonFootprint({interpolation: 'spline'}); + + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't2.micro', + }, + ]; + + const result = await output.execute(inputs); + + expect.assertions(1); + expect(result).toStrictEqual([ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't2.micro', + 'carbon-embodied': 0.8784841133942161, + energy: 0.004900000000000001, + }, + ]); + }); + + it('executes with multiple valid data in the input.', async () => { + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + }, + ]; + const result = await output.execute(inputs); + + expect.assertions(1); + + expect(result).toStrictEqual([ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + energy: 0.0019435697915529846, + 'carbon-embodied': 0.9577090468036529, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + energy: 0.0046062540925461085, + 'carbon-embodied': 0.9577090468036529, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + energy: 0.007934609468787513, + 'carbon-embodied': 0.9577090468036529, + }, + ]); + }); + + it('throws an error when `interpolation` persists in the config but the vendor is not `aws`.', async () => { + const output = CloudCarbonFootprint({interpolation: 'spline'}); + + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + }, + ]; + + try { + await output.execute(inputs); + } catch (error) { + expect(error).toBeInstanceOf(UnsupportedValueError); + expect(error).toEqual( + new UnsupportedValueError( + 'CloudCarbonFootprint: Interpolation spline method is not supported.' + ) + ); + } + }); + }); + + describe('init with `azure` value of `cloud/vendor`.', () => { + it('executes with multiple valid data in the input.', async () => { + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + }, + ]; + expect.assertions(1); + + const result = await output.execute(inputs); + + expect(result).toStrictEqual([ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + energy: 0.0019435697915529846, + 'carbon-embodied': 0.3195276826484018, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + energy: 0.0046062540925461085, + 'carbon-embodied': 0.3195276826484018, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'azure', + 'cloud/instance-type': 'D2 v4', + energy: 0.007934609468787513, + 'carbon-embodied': 0.3195276826484018, + }, + ]); + }); + }); + + describe('init with `gcp` value of `cloud/vendor`.', () => { + it('executes with multiple valid data in the input.', async () => { + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + }, + ]; + + const result = await output.execute(inputs); + + expect.assertions(1); + + expect(result).toStrictEqual([ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + energy: 0.0018785992503765141, + 'carbon-embodied': 0.8421000998858448, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + energy: 0.004281401386663755, + 'carbon-embodied': 0.8421000998858448, + }, + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 100, + 'cloud/vendor': 'gcp', + 'cloud/instance-type': 'n2-standard-2', + energy: 0.0072849040570228075, + 'carbon-embodied': 0.8421000998858448, + }, + ]); + }); + }); + }); + + it('throws an error when `cloud/instance-type` value is wrong.', async () => { + const errorMessage = + 'CloudCarbonFootprint: Instance type t5.micro is not supported.'; + + expect.assertions(2); + + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't5.micro', + }, + ]; + + try { + await output.execute(inputs); + } catch (error) { + expect(error).toEqual(new UnsupportedValueError(errorMessage)); + expect(error).toBeInstanceOf(UnsupportedValueError); + } + }); + + it('throws an error when `cloud/vendor` value is wrong.', async () => { + const errorMessage = + "\"cloud/vendor\" parameter is invalid enum value. expected 'aws' | 'gcp' | 'azure', received 'aws2'. Error code: invalid_enum_value."; + + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'aws2', + 'cloud/instance-type': 't2.micro', + }, + ]; + expect.assertions(2); + + try { + await output.execute(inputs); + } catch (error) { + expect(error).toEqual(new InputValidationError(errorMessage)); + expect(error).toBeInstanceOf(InputValidationError); + } + }); + + it('throws an error when `interpolation` value is wrong.', async () => { + const errorMessage = + "\"interpolation\" parameter is invalid enum value. expected 'spline' | 'linear', received 'linear2'. Error code: invalid_enum_value."; + + const output = CloudCarbonFootprint({interpolation: 'linear2'}); + + expect.assertions(2); + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 10, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 'm5n.large', + }, + ]; + expect.assertions(2); + + try { + await output.execute(inputs); + } catch (error) { + expect(error).toEqual(new InputValidationError(errorMessage)); + expect(error).toBeInstanceOf(InputValidationError); + } + }); + }); +}); diff --git a/src/__tests__/unit/lib/ccf/ccf-usage.test.ts b/src/__tests__/unit/lib/ccf/ccf-usage.test.ts new file mode 100644 index 0000000..00246d1 --- /dev/null +++ b/src/__tests__/unit/lib/ccf/ccf-usage.test.ts @@ -0,0 +1,39 @@ +import {CloudCarbonFootprint} from '../../../../lib/ccf/index'; + +import {ERRORS} from '../../../../util/errors'; +const {UnsupportedValueError} = ERRORS; + +jest.mock('../../../../lib/ccf/aws-use.json', () => [{}], {virtual: true}); + +describe('lib/ccf: ', () => { + describe('CloudCarbonFootprint: ', () => { + const output = CloudCarbonFootprint(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('throws an error when the architecture in not supported in `aws`.', async () => { + const inputs = [ + { + timestamp: '2021-01-01T00:00:00Z', + duration: 3600, + 'cpu/utilization': 50, + 'cloud/vendor': 'aws', + 'cloud/instance-type': 't2.micro', + }, + ]; + + try { + await output.execute(inputs); + } catch (error) { + expect(error).toBeInstanceOf(UnsupportedValueError); + expect(error).toEqual( + new UnsupportedValueError( + "CloudCarbonFootprint: Architecture 'Graviton' is not supported." + ) + ); + } + }); + }); +}); diff --git a/src/__tests__/unit/lib/ccf/index.test.ts b/src/__tests__/unit/lib/ccf/index.test.ts index f3c52e0..9579a76 100644 --- a/src/__tests__/unit/lib/ccf/index.test.ts +++ b/src/__tests__/unit/lib/ccf/index.test.ts @@ -1,337 +1,2 @@ -import {CloudCarbonFootprint} from '../../../../lib/ccf/index'; - -import {ERRORS} from '../../../../util/errors'; - -const {InputValidationError, UnsupportedValueError} = ERRORS; - -describe('lib/ccf: ', () => { - describe('CloudCarbonFootprint: ', () => { - const output = CloudCarbonFootprint(); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('init CloudCarbonFootprint: ', () => { - it('initalizes object with properties.', async () => { - expect(output).toHaveProperty('metadata'); - expect(output).toHaveProperty('execute'); - }); - }); - - describe('execute(): ', () => { - describe('init with `aws` value of `cloud/vendor`.', () => { - it('executes with valid data, without `interpolation` config.', async () => { - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 't2.micro', - }, - ]; - - const result = await output.execute(inputs); - - expect.assertions(1); - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 't2.micro', - 'carbon-embodied': 0.8784841133942161, - energy: 0.0023031270462730543, - }, - ]); - }); - - it('executes with valid data, with `interpolation` config.', async () => { - const output = CloudCarbonFootprint({interpolation: 'spline'}); - - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 't2.micro', - }, - ]; - - const result = await output.execute(inputs); - - expect.assertions(1); - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 't2.micro', - 'carbon-embodied': 0.8784841133942161, - energy: 0.004900000000000001, - }, - ]); - }); - - it('executes with multiple valid data in the input.', async () => { - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - }, - ]; - const result = await output.execute(inputs); - - expect.assertions(1); - - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - energy: 0.0019435697915529846, - 'carbon-embodied': 0.9577090468036529, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - energy: 0.0046062540925461085, - 'carbon-embodied': 0.9577090468036529, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - energy: 0.007934609468787513, - 'carbon-embodied': 0.9577090468036529, - }, - ]); - }); - }); - - describe('init with `azure` value of `cloud/vendor`.', () => { - it('executes with multiple valid data in the input.', async () => { - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - }, - ]; - expect.assertions(1); - - const result = await output.execute(inputs); - - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - energy: 0.0019435697915529846, - 'carbon-embodied': 0.3195276826484018, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - energy: 0.0046062540925461085, - 'carbon-embodied': 0.3195276826484018, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'azure', - 'cloud/instance-type': 'D2 v4', - energy: 0.007934609468787513, - 'carbon-embodied': 0.3195276826484018, - }, - ]); - }); - }); - - describe('init with `gcp` value of `cloud/vendor`.', () => { - it('executes with multiple valid data in the input.', async () => { - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - }, - ]; - - const result = await output.execute(inputs); - - expect.assertions(1); - - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - energy: 0.0018785992503765141, - 'carbon-embodied': 0.8421000998858448, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 50, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - energy: 0.004281401386663755, - 'carbon-embodied': 0.8421000998858448, - }, - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 100, - 'cloud/vendor': 'gcp', - 'cloud/instance-type': 'n2-standard-2', - energy: 0.0072849040570228075, - 'carbon-embodied': 0.8421000998858448, - }, - ]); - }); - }); - }); - - it('throws an error when `cloud/instance-type` value is wrong.', async () => { - const errorMessage = - 'CloudCarbonFootprint: Instance type t5.micro is not supported.'; - - expect.assertions(2); - - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 't5.micro', - }, - ]; - - try { - await output.execute(inputs); - } catch (error) { - expect(error).toEqual(new UnsupportedValueError(errorMessage)); - expect(error).toBeInstanceOf(UnsupportedValueError); - } - }); - - it('throws an error when `cloud/vendor` value is wrong.', async () => { - const errorMessage = - "\"cloud/vendor\" parameter is invalid enum value. expected 'aws' | 'gcp' | 'azure', received 'aws2'. Error code: invalid_enum_value."; - - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'aws2', - 'cloud/instance-type': 't2.micro', - }, - ]; - expect.assertions(2); - - try { - await output.execute(inputs); - } catch (error) { - expect(error).toEqual(new InputValidationError(errorMessage)); - expect(error).toBeInstanceOf(InputValidationError); - } - }); - - it('throws an error when `interpolation` value is wrong.', async () => { - const errorMessage = - "\"interpolation\" parameter is invalid enum value. expected 'spline' | 'linear', received 'linear2'. Error code: invalid_enum_value."; - - const output = CloudCarbonFootprint({interpolation: 'linear2'}); - - expect.assertions(2); - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 3600, - 'cpu/utilization': 10, - 'cloud/vendor': 'aws', - 'cloud/instance-type': 'm5n.large', - }, - ]; - expect.assertions(2); - - try { - await output.execute(inputs); - } catch (error) { - expect(error).toEqual(new InputValidationError(errorMessage)); - expect(error).toBeInstanceOf(InputValidationError); - } - }); - }); -}); +import './ccf-main.test'; +import './ccf-usage.test';