From 9da09b6ba3db0af434095fde0e22266d549fc2bc Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:15:59 +0400 Subject: [PATCH 01/38] feat(util): update error usage in plugin storage --- src/util/plugin-storage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/plugin-storage.ts b/src/util/plugin-storage.ts index 7667d5e07..15582e9f3 100644 --- a/src/util/plugin-storage.ts +++ b/src/util/plugin-storage.ts @@ -4,7 +4,7 @@ import {STRINGS} from '../config'; import {PluginInterface} from '../types/interface'; import {PluginStorage} from '../types/plugin-storage'; -const {PluginInitalizationError} = ERRORS; +const {PluginInitializationError} = ERRORS; const {NOT_INITALIZED_PLUGIN} = STRINGS; /** @@ -21,7 +21,7 @@ export const pluginStorage = () => { const plugin = storage[name]; if (!plugin) { - throw new PluginInitalizationError(NOT_INITALIZED_PLUGIN(name)); + throw new PluginInitializationError(NOT_INITALIZED_PLUGIN(name)); } return plugin; From e769eaaed7c4d22b04399fe483fdee0caa9383b5 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:16:40 +0400 Subject: [PATCH 02/38] feat(util): drop build error message helper --- src/util/helpers.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/util/helpers.ts b/src/util/helpers.ts index b15863cef..c2b644d32 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -1,24 +1,16 @@ import {createInterface} from 'node:readline/promises'; import {exec} from 'node:child_process'; import {promisify} from 'node:util'; -import {ErrorFormatParams} from '../types/helpers'; + import {ERRORS} from './errors'; import {logger} from './logger'; + import {STRINGS} from '../config'; import {Difference} from '../types/lib/compare'; -const {ISSUE_TEMPLATE} = STRINGS; - -/** - * Formats given error according to class instance, scope and message. - */ -export const buildErrorMessage = - (classInstanceName: string) => (params: ErrorFormatParams) => { - const {scope, message} = params; - - return `${classInstanceName}${scope ? `(${scope})` : ''}: ${message}.`; - }; +const {ISSUE_TEMPLATE, MISSING_MANIFEST_IN_STDIN} = STRINGS; +const {CliInputError} = ERRORS; /** * Impact engine error handler. Logs errors and appends issue template if error is unknown. @@ -177,7 +169,7 @@ export const parseManifestFromStdin = async () => { const match = regex.exec(pipedSourceManifest); if (!match) { - throw new Error('Manifest not found in STDIN.'); + throw new CliInputError(MISSING_MANIFEST_IN_STDIN); } return match![1]; From 4251823d72d00dfd295841c5ded980009c65d1c4 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:17:20 +0400 Subject: [PATCH 03/38] feat(util): make error classes granular --- src/util/errors.ts | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/util/errors.ts b/src/util/errors.ts index a65a603be..f4f067d77 100644 --- a/src/util/errors.ts +++ b/src/util/errors.ts @@ -1,19 +1,33 @@ const CUSTOM_ERRORS = [ 'CliInputError', - 'ConfigNotFoundError', - 'ConfigValidationError', - 'ExhaustError', - 'FileNotFoundError', - 'MakeDirectoryError', 'ManifestValidationError', - 'ModuleInitializationError', 'InputValidationError', - 'InvalidAggregationParamsError', 'InvalidGroupingError', - 'PluginCredentialError', - 'PluginInitalizationError', 'WriteFileError', - 'ConfigNotFoundError', + /** More specific errors */ + 'ParseCliParamsError', + 'CliSourceFileError', + 'CliTargetFileError', + 'InvalidAggregationMethodError', + 'MissingAggregationParamError', + 'MissingPluginMethodError', + 'MissingPluginPathError', + 'PluginInitializationError', + 'InvalidExhaustPluginError', + /** Plugins */ + 'GlobalConfigError', + 'MissingInputDataError', + 'ProcessExecutionError', + 'RegexMismatchError', + 'FetchingFileError', + 'ReadFileError', + 'MissingCSVColumnError', + 'QueryDataNotFoundError', + 'InvalidDateInInputError', + 'InvalidPaddingError', + 'InvalidInputError', + 'ExhaustOutputArgError', + 'CSVParseError', ] as const; type CustomErrors = { From 78972b1cd8d84fe66699462b45799f7a626ec950 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:17:54 +0400 Subject: [PATCH 04/38] feat(util): update errors usage in args --- src/util/args.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/util/args.ts b/src/util/args.ts index 56466fd48..06ee99e93 100644 --- a/src/util/args.ts +++ b/src/util/args.ts @@ -10,7 +10,12 @@ import {CONFIG, STRINGS} from '../config'; import {IFDiffArgs, IEArgs, ProcessArgsOutputs} from '../types/process-args'; import {LoadDiffParams} from '../types/util/args'; -const {CliInputError} = ERRORS; +const { + CliInputError, + ParseCliParamsError, + CliTargetFileError, + CliSourceFileError, +} = ERRORS; const {IE, IF_DIFF} = CONFIG; @@ -83,10 +88,10 @@ export const parseIEProcessArgs = (): ProcessArgsOutputs => { }; } - throw new CliInputError(FILE_IS_NOT_YAML); + throw new CliSourceFileError(FILE_IS_NOT_YAML); } - throw new CliInputError(MANIFEST_IS_MISSING); + throw new CliSourceFileError(MANIFEST_IS_MISSING); }; /** -- IF Diff -- */ @@ -99,7 +104,7 @@ const validateAndParseIfDiffArgs = () => { return parse(IF_DIFF.ARGS, IF_DIFF.HELP); } catch (error) { if (error instanceof Error) { - throw new CliInputError(error.message); + throw new ParseCliParamsError(error.message); } throw error; @@ -114,7 +119,7 @@ export const parseIfDiffArgs = () => { if (target) { if (source && !checkIfFileIsYaml(source)) { - throw new CliInputError(SOURCE_IS_NOT_YAML); + throw new CliSourceFileError(SOURCE_IS_NOT_YAML); } if (checkIfFileIsYaml(target)) { @@ -129,7 +134,7 @@ export const parseIfDiffArgs = () => { return response; } - throw new CliInputError(TARGET_IS_NOT_YAML); + throw new CliTargetFileError(TARGET_IS_NOT_YAML); } throw new CliInputError(INVALID_TARGET); From 62f70d5123eaa5a4fd12fed9cbe01fcdc0916ecc Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:18:22 +0400 Subject: [PATCH 05/38] feat(util): update errors usage in aggregation helper --- src/util/aggregation-helper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/aggregation-helper.ts b/src/util/aggregation-helper.ts index 3be503ffc..0a8b3c136 100644 --- a/src/util/aggregation-helper.ts +++ b/src/util/aggregation-helper.ts @@ -6,7 +6,7 @@ import {CONFIG, STRINGS} from '../config'; import {AggregationResult} from '../types/aggregation'; import {PluginParams} from '../types/interface'; -const {InvalidAggregationParamsError} = ERRORS; +const {InvalidAggregationMethodError, MissingAggregationParamError} = ERRORS; const {INVALID_AGGREGATION_METHOD, METRIC_MISSING} = STRINGS; const {AGGREGATION_ADDITIONAL_PARAMS} = CONFIG; @@ -19,7 +19,7 @@ const checkIfMetricsAreValid = (metrics: string[]) => { const method = parameterize.getAggregationMethod(metric); if (method === 'none') { - throw new InvalidAggregationParamsError( + throw new InvalidAggregationMethodError( INVALID_AGGREGATION_METHOD(metric) ); } @@ -41,7 +41,7 @@ export const aggregateInputsIntoOne = ( return inputs.reduce((acc, input, index) => { for (const metric of extendedMetrics) { if (!(metric in input)) { - throw new InvalidAggregationParamsError(METRIC_MISSING(metric, index)); + throw new MissingAggregationParamError(METRIC_MISSING(metric, index)); } /** Checks if metric is timestamp or duration, then adds to aggregated value. */ From 3269fc9b62347945f6f5048d06bfa75e67337ba7 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:22:12 +0400 Subject: [PATCH 06/38] revert(types): drop helpers --- src/types/helpers.ts | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/types/helpers.ts diff --git a/src/types/helpers.ts b/src/types/helpers.ts deleted file mode 100644 index ed413174b..000000000 --- a/src/types/helpers.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ErrorFormatParams = { - scope?: string; - message: string; -}; From 1c826ea2a3e563c332fd745557e2415c1505a052 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:25:44 +0400 Subject: [PATCH 07/38] feat(lib): update errors usage in load --- src/lib/load.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/load.ts b/src/lib/load.ts index 447153068..e1a0cb465 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -12,7 +12,7 @@ import {Parameters} from '../types/parameters'; import {LoadDiffParams} from '../types/util/args'; import {Manifest} from '../types/manifest'; -const {CliInputError} = ERRORS; +const {CliSourceFileError} = ERRORS; const {INVALID_SOURCE} = STRINGS; @@ -43,7 +43,7 @@ export const loadIfDiffFiles = async (params: LoadDiffParams) => { const pipedSourceManifest = await parseManifestFromStdin(); if (!sourcePath && !pipedSourceManifest) { - throw new CliInputError(INVALID_SOURCE); + throw new CliSourceFileError(INVALID_SOURCE); } const loadFromSource = From 85a66dd93e9a79b75e1774c32443b00bbf1a698e Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:26:03 +0400 Subject: [PATCH 08/38] feat(lib): update errors usage in initalize --- src/lib/initialize.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/lib/initialize.ts b/src/lib/initialize.ts index d6bf7c184..e20d5f53b 100644 --- a/src/lib/initialize.ts +++ b/src/lib/initialize.ts @@ -11,7 +11,11 @@ import {PluginInterface} from '../types/interface'; import {GlobalPlugins, PluginOptions} from '../types/manifest'; import {PluginStorageInterface} from '../types/plugin-storage'; -const {ModuleInitializationError, PluginCredentialError} = ERRORS; +const { + PluginInitializationError, + MissingPluginMethodError, + MissingPluginPathError, +} = ERRORS; const {GITHUB_PATH, NATIVE_PLUGIN} = CONFIG; const {MISSING_METHOD, MISSING_PATH, NOT_NATIVE_PLUGIN, INVALID_MODULE_PATH} = @@ -21,14 +25,11 @@ const {MISSING_METHOD, MISSING_PATH, NOT_NATIVE_PLUGIN, INVALID_MODULE_PATH} = * Imports module by given `path`. */ const importModuleFrom = async (path: string) => { - try { - const module = await import(path); + const module = await import(path).catch(error => { + throw new PluginInitializationError(INVALID_MODULE_PATH(path, error)); + }); - return module; - } catch (error) { - logger.error(error); - throw new ModuleInitializationError(INVALID_MODULE_PATH(path)); - } + return module; }; /** @@ -71,11 +72,11 @@ const initPlugin = async ( const {method, path, 'global-config': globalConfig} = initPluginParams; if (!method) { - throw new PluginCredentialError(MISSING_METHOD); + throw new MissingPluginMethodError(MISSING_METHOD); } if (!path) { - throw new PluginCredentialError(MISSING_PATH); + throw new MissingPluginPathError(MISSING_PATH); } const plugin = await handModule(method, path); From c39cbab19fd81a24a09e8974fd736683350a155b Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 01:26:21 +0400 Subject: [PATCH 09/38] feat(lib): update errors usage in exhaust --- src/lib/exhaust.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/exhaust.ts b/src/lib/exhaust.ts index 5c80b0a5a..98eaf7625 100644 --- a/src/lib/exhaust.ts +++ b/src/lib/exhaust.ts @@ -14,7 +14,7 @@ import {ExhaustPluginInterface} from '../types/exhaust-plugin-interface'; import {Context} from '../types/manifest'; import {Options} from '../types/process-args'; -const {ExhaustError} = ERRORS; +const {InvalidExhaustPluginError} = ERRORS; const {INVALID_EXHAUST_PLUGIN} = STRINGS; /** @@ -35,7 +35,7 @@ const initializeExhaustPlugin = (name: string): ExhaustPluginInterface => { case 'csv-raw': return ExportCSVRaw(); default: - throw new ExhaustError(INVALID_EXHAUST_PLUGIN(name)); + throw new InvalidExhaustPluginError(INVALID_EXHAUST_PLUGIN(name)); } }; From 731670336963afd75b2e9167f9ebb46cb1c0ab71 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 12:26:58 +0400 Subject: [PATCH 10/38] feat(config): update strings to include all messages --- src/config/strings.ts | 59 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/config/strings.ts b/src/config/strings.ts index 04551b9b9..a96e11184 100644 --- a/src/config/strings.ts +++ b/src/config/strings.ts @@ -17,12 +17,12 @@ Incubation projects are experimental, offer no support guarantee, have minimal g NOT_NATIVE_PLUGIN: (path: string) => ` You are using plugin ${path} which is not part of the Impact Framework standard library. You should do your own research to ensure the plugins are up to date and accurate. They may not be actively maintained.`, - SOMETHING_WRONG: 'Something wrong with cli arguments. Please check docs.', ISSUE_TEMPLATE: ` Impact Framework is an alpha release from the Green Software Foundation and is released to capture early feedback. If you'd like to offer some feedback, please use this issue template: https://github.com/Green-Software-Foundation/if/issues/new?assignees=&labels=feedback&projects=&template=feedback.md&title=Feedback+-+`, - INVALID_MODULE_PATH: (path: string) => - `Provided module: '${path}' is invalid or not found.`, + INVALID_MODULE_PATH: (path: string, error?: any) => + `Provided module \`${path}\` is invalid or not found. ${error ?? ''} +`, INVALID_TIME_NORMALIZATION: 'Start time or end time is missing.', UNEXPECTED_TIME_CONFIG: 'Unexpected node-level config provided for time-sync plugin.', @@ -33,7 +33,6 @@ https://github.com/Green-Software-Foundation/if/issues/new?assignees=&labels=fee `Avoiding padding at ${ start && end ? 'start and end' : start ? 'start' : 'end' }`, - INVALID_OBSERVATION_OVERLAP: 'Observation timestamps overlap.', INVALID_AGGREGATION_METHOD: (metric: string) => `Aggregation is not possible for given ${metric} since method is 'none'.`, METRIC_MISSING: (metric: string, index: number) => @@ -56,4 +55,56 @@ Note that for the '--output' option you also need to define the output type in y TARGET_IS_NOT_YAML: 'Given target is not in yaml format.', INVALID_TARGET: 'Target is invalid.', INVALID_SOURCE: 'Source is invalid.', + MISSING_MANIFEST_IN_STDIN: 'Manifest not found in STDIN.', + /** Plugin messages */ + MISSING_GLOBAL_CONFIG: 'Global config is not provided.', + MISSING_INPUT_DATA: (param: string) => + `${param} is missing from the input array.`, + NOT_NUMERIC_VALUE: (str: any) => `${str} is not numberic.`, + MISSING_FUNCTIONAL_UNIT_CONFIG: + '`functional-unit` should be provided in your global config', + MISSING_FUNCTIONAL_UNIT_INPUT: + '`functional-unit` value is missing from input data or it is not a positive integer', + REGEX_MISMATCH: (input: any, match: string) => + `\`${input}\` does not match the ${match} regex expression`, + SCI_EMBODIED_ERROR: (unit: string) => + `invalid number. please provide it as \`${unit}\` to input`, + MISSING_MIN_MAX: 'Config is missing min or max value', + INVALID_MIN_MAX: (name: string) => + `Min value should not be greater than or equal to max value of ${name}`, + FILE_FETCH_FAILED: ( + filepath: string, + message: string + ) => `Failed fetching the file: ${filepath}. +${message}`, + FILE_READ_FAILED: ( + filepath: string, + error: string + ) => `Failed reading the file: ${filepath}. +${error}`, + MISSING_CSV_COLUMN: (columnName: string) => + `There is no column with the name: ${columnName}.`, + NO_QUERY_DATA: + 'One or more of the given query parameters are not found in the target CSV file column headers.', + INVALID_DATE_TYPE: (date: any) => + `Unexpected date datatype: ${typeof date}: ${date}`, + INVALID_OBSERVATION_OVERLAP: + 'Observation timestamps overlap, please check inputs.', + /** Exhaust messages */ + OUTPUT_REQUIRED: + 'Output path is required, please make sure output is configured properly.', + CSV_EXPORT: + 'CSV export criteria is not found in output path. Please append it after --output #.', + WRITE_CSV_ERROR: (outputPath: string, error: any) => + `Failed to write CSV file to ${outputPath}: ${error}`, + INVALID_NAME: + '`name` global config parameter is empty or contains all spaces', + START_LOWER_END: '`start-time` should be lower than `end-time`', + TIMESTAMP_REQUIRED: (index: number) => `required in input[${index}]`, + INVALID_DATETIME: (index: number) => `invalid datetime in input[${index}]`, + X_Y_EQUAL: 'The length of `x` and `y` should be equal', + ARRAY_LENGTH_NON_EMPTY: + 'the length of the input arrays must be greater than 1', + WITHIN_THE_RANGE: + 'The target x value must be within the range of the given x values', }; From d9c047ef49b59e44f360eef6b43d664369421b27 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:54:20 +0400 Subject: [PATCH 11/38] feat(builtins): update errors usage in sum --- src/builtins/sum/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/builtins/sum/index.ts b/src/builtins/sum/index.ts index 8eec61c37..9944373a8 100644 --- a/src/builtins/sum/index.ts +++ b/src/builtins/sum/index.ts @@ -1,13 +1,15 @@ import {z} from 'zod'; -import {ExecutePlugin, PluginParams} from '../../types/interface'; - import {validate} from '../../util/validations'; import {ERRORS} from '../../util/errors'; +import {STRINGS} from '../../config'; + +import {ExecutePlugin, PluginParams} from '../../types/interface'; import {SumConfig} from './types'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +const {GlobalConfigError, MissingInputDataError} = ERRORS; +const {MISSING_INPUT_DATA, MISSING_GLOBAL_CONFIG} = STRINGS; export const Sum = (globalConfig: SumConfig): ExecutePlugin => { const metadata = { @@ -37,7 +39,7 @@ export const Sum = (globalConfig: SumConfig): ExecutePlugin => { */ const validateGlobalConfig = () => { if (!globalConfig) { - throw new ConfigNotFoundError('Global config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const globalConfigSchema = z.object({ @@ -60,9 +62,7 @@ export const Sum = (globalConfig: SumConfig): ExecutePlugin => { ) => { inputParameters.forEach(metricToSum => { if (!input[metricToSum]) { - throw new InputValidationError( - `${metricToSum} is missing from the input array.` - ); + throw new MissingInputDataError(MISSING_INPUT_DATA(metricToSum)); } }); From d0452663110a6b71f3f6ca1a4a1e162ccf99d79d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:54:49 +0400 Subject: [PATCH 12/38] feat(builtins): update errors usage in mock observations, do cosmetic fixes --- .../helpers/common-generator.ts | 27 ++++----- .../helpers/rand-int-generator.ts | 56 ++++++++----------- src/builtins/mock-observations/index.ts | 6 +- 3 files changed, 35 insertions(+), 54 deletions(-) diff --git a/src/builtins/mock-observations/helpers/common-generator.ts b/src/builtins/mock-observations/helpers/common-generator.ts index 24bea4c94..ee4909dcd 100644 --- a/src/builtins/mock-observations/helpers/common-generator.ts +++ b/src/builtins/mock-observations/helpers/common-generator.ts @@ -1,37 +1,30 @@ -import {KeyValuePair} from '../../../types/common'; import {ERRORS} from '../../../util/errors'; -import {buildErrorMessage} from '../../../util/helpers'; -import {Generator} from '../interfaces'; - -const {InputValidationError} = ERRORS; +import {STRINGS} from '../../../config'; -export const CommonGenerator = (config: KeyValuePair): Generator => { - const errorBuilder = buildErrorMessage(CommonGenerator.name); +import {Generator} from '../interfaces'; - /** - * Creates new copy of the given `object`. - */ - const copyObject = (object: T): T => ({...object}); +const {GlobalConfigError} = ERRORS; +const {MISSING_GLOBAL_CONFIG} = STRINGS; +export const CommonGenerator = (config: Record): Generator => { /** + * Generates next value by copying the validated config. * Validates the provided config is not null or empty. - * returns a copy of the validated config, otherwise throws an InputValidationError. + * Returns a copy of the validated config, otherwise throws an GlobalConfigError. */ const validateConfig = (config: object) => { if (!config || Object.keys(config).length === 0) { - throw new InputValidationError( - errorBuilder({message: 'Config must not be null or empty'}) - ); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } - return copyObject(config); + return structuredClone(config); }; /** * Generates next value by copying the validated config. */ - const next = (): Object => copyObject(validateConfig(config)); + const next = (): Object => validateConfig(config); return { next, diff --git a/src/builtins/mock-observations/helpers/rand-int-generator.ts b/src/builtins/mock-observations/helpers/rand-int-generator.ts index e27d694c3..a48e399c8 100644 --- a/src/builtins/mock-observations/helpers/rand-int-generator.ts +++ b/src/builtins/mock-observations/helpers/rand-int-generator.ts @@ -1,70 +1,57 @@ -import {KeyValuePair} from '../../../types/common'; import {ERRORS} from '../../../util/errors'; -import {buildErrorMessage} from '../../../util/helpers'; + +import {STRINGS} from '../../../config'; import {Generator} from '../interfaces'; import {RandIntGeneratorParams} from '../types'; -const {InputValidationError} = ERRORS; +const {GlobalConfigError} = ERRORS; + +const {MISSING_GLOBAL_CONFIG, MISSING_MIN_MAX, INVALID_MIN_MAX, INVALID_NAME} = + STRINGS; export const RandIntGenerator = ( name: string, - config: KeyValuePair + config: Record ): Generator => { - const errorBuilder = buildErrorMessage(RandIntGenerator.name); - const next = () => ({ [validatedName]: generateRandInt(getFieldToPopulate()), }); const validateName = (name: string | null): string => { if (!name || name.trim() === '') { - throw new InputValidationError( - errorBuilder({ - message: '`name` is empty or all spaces', - }) - ); + throw new GlobalConfigError(INVALID_NAME); } + return name; }; - const validateConfig = (config: KeyValuePair): {min: number; max: number} => { + const validateConfig = ( + config: Record + ): {min: number; max: number} => { if (!config || Object.keys(config).length === 0) { - throw new InputValidationError( - errorBuilder({ - message: 'Config must not be null or empty', - }) - ); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } if (!config.min || !config.max) { - throw new InputValidationError( - errorBuilder({ - message: 'Config is missing min or max', - }) - ); + throw new GlobalConfigError(MISSING_MIN_MAX); } if (config.min >= config.max) { - throw new InputValidationError( - errorBuilder({ - message: `Min value should not be greater than or equal to max value of ${validatedName}`, - }) - ); + throw new GlobalConfigError(INVALID_MIN_MAX(validatedName)); } + return {min: config.min, max: config.max}; }; const validatedName = validateName(name); const validatedConfig = validateConfig(config); - const getFieldToPopulate = () => { - return { - name: validatedName, - min: validatedConfig.min, - max: validatedConfig.max, - }; - }; + const getFieldToPopulate = () => ({ + name: validatedName, + min: validatedConfig.min, + max: validatedConfig.max, + }); const generateRandInt = ( randIntGenerator: RandIntGeneratorParams @@ -73,6 +60,7 @@ export const RandIntGenerator = ( const scaledNumber = randomNumber * (randIntGenerator.max - randIntGenerator.min) + randIntGenerator.min; + return Math.trunc(scaledNumber); }; diff --git a/src/builtins/mock-observations/index.ts b/src/builtins/mock-observations/index.ts index 2d65f4643..b535da757 100644 --- a/src/builtins/mock-observations/index.ts +++ b/src/builtins/mock-observations/index.ts @@ -1,13 +1,13 @@ import {DateTime, Duration} from 'luxon'; import {z} from 'zod'; -import {ExecutePlugin, PluginParams} from '../../types/interface'; -import {ConfigParams, KeyValuePair} from '../../types/common'; - import {validate} from '../../util/validations'; import {CommonGenerator} from './helpers/common-generator'; import {RandIntGenerator} from './helpers/rand-int-generator'; + +import {ExecutePlugin, PluginParams} from '../../types/interface'; +import {ConfigParams, KeyValuePair} from '../../types/common'; import {Generator} from './interfaces/index'; import {ObservationParams} from './types'; From c4ed3d87726c386c74194ba20a825759438c64e1 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:55:20 +0400 Subject: [PATCH 13/38] feat(builtins): update errors usage in subtract --- src/builtins/subtract/index.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/builtins/subtract/index.ts b/src/builtins/subtract/index.ts index 0af02898a..25f5d8522 100644 --- a/src/builtins/subtract/index.ts +++ b/src/builtins/subtract/index.ts @@ -1,16 +1,17 @@ import {z} from 'zod'; import {ERRORS} from '../../util/errors'; -import {buildErrorMessage} from '../../util/helpers'; import {validate} from '../../util/validations'; +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams} from '../../types/interface'; import {SubtractConfig} from './types'; const {InputValidationError} = ERRORS; +const {MISSING_INPUT_DATA, NOT_NUMERIC_VALUE} = STRINGS; export const Subtract = (globalConfig: SubtractConfig): ExecutePlugin => { - const errorBuilder = buildErrorMessage(Subtract.name); const metadata = { kind: 'execute', }; @@ -47,21 +48,13 @@ export const Subtract = (globalConfig: SubtractConfig): ExecutePlugin => { const validateParamExists = (input: PluginParams, param: string) => { if (input[param] === undefined) { - throw new InputValidationError( - errorBuilder({ - message: `${param} is missing from the input array`, - }) - ); + throw new InputValidationError(MISSING_INPUT_DATA(param)); } }; const validateNumericString = (str: string) => { if (isNaN(+Number(str))) { - throw new InputValidationError( - errorBuilder({ - message: `${str} is not numberic`, - }) - ); + throw new InputValidationError(NOT_NUMERIC_VALUE(str)); } }; @@ -73,6 +66,7 @@ export const Subtract = (globalConfig: SubtractConfig): ExecutePlugin => { 'input-parameters': inputParameters, 'output-parameter': outputParameter, } = validateGlobalConfig(); + return inputs.map(input => { validateSingleInput(input, inputParameters); @@ -88,6 +82,7 @@ export const Subtract = (globalConfig: SubtractConfig): ExecutePlugin => { */ const calculateDiff = (input: PluginParams, inputParameters: string[]) => { const [firstItem, ...restItems] = inputParameters; + return restItems.reduce( (accumulator, metricToSubtract) => accumulator - input[metricToSubtract], input[firstItem] // Starting accumulator with the value of the first item From c21fb0f01f3d23c7652c3e3c9e0b080697659c17 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:55:42 +0400 Subject: [PATCH 14/38] feat(builtins): update errors usage in shell --- src/builtins/shell/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/builtins/shell/index.ts b/src/builtins/shell/index.ts index 43b1c224c..c78f668e2 100644 --- a/src/builtins/shell/index.ts +++ b/src/builtins/shell/index.ts @@ -8,7 +8,7 @@ import {ConfigParams} from '../../types/common'; import {validate} from '../../util/validations'; import {ERRORS} from '../../util/errors'; -const {InputValidationError} = ERRORS; +const {ProcessExecutionError} = ERRORS; export const Shell = (globalConfig: ConfigParams): ExecutePlugin => { const metadata = { @@ -55,7 +55,7 @@ export const Shell = (globalConfig: ConfigParams): ExecutePlugin => { return {outputs}; } catch (error: any) { - throw new InputValidationError(error.message); + throw new ProcessExecutionError(error.message); } }; From 8c3990189e56ac0541b1cee9f0f901d2f4bc3b9f Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:55:51 +0400 Subject: [PATCH 15/38] feat(builtins): update errors usage in sci-embodied --- src/builtins/sci-embodied/index.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/builtins/sci-embodied/index.ts b/src/builtins/sci-embodied/index.ts index 162099860..02f897584 100644 --- a/src/builtins/sci-embodied/index.ts +++ b/src/builtins/sci-embodied/index.ts @@ -1,8 +1,12 @@ import {z} from 'zod'; +import {validate, allDefined} from '../../util/validations'; + +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams} from '../../types/interface'; -import {validate, allDefined} from '../../util/validations'; +const {SCI_EMBODIED_ERROR} = STRINGS; export const SciEmbodied = (): ExecutePlugin => { const metadata = { @@ -55,9 +59,6 @@ export const SciEmbodied = (): ExecutePlugin => { * Checks for required fields in input. */ const validateInput = (input: PluginParams) => { - const errorMessage = (unit: string) => - `not a valid number in input. Please provide it as \`${unit}\``; - const commonSchemaPart = (errorMessage: (unit: string) => string) => ({ 'device/emissions-embodied': z .number({ @@ -81,13 +82,13 @@ export const SciEmbodied = (): ExecutePlugin => { const vcpusSchemaPart = { 'vcpus-allocated': z .number({ - invalid_type_error: errorMessage('count'), + invalid_type_error: SCI_EMBODIED_ERROR('count'), }) .gte(0) .min(0), 'vcpus-total': z .number({ - invalid_type_error: errorMessage('count'), + invalid_type_error: SCI_EMBODIED_ERROR('count'), }) .gte(0) .min(0), @@ -96,24 +97,24 @@ export const SciEmbodied = (): ExecutePlugin => { const resourcesSchemaPart = { 'resources-reserved': z .number({ - invalid_type_error: errorMessage('count'), + invalid_type_error: SCI_EMBODIED_ERROR('count'), }) .gte(0) .min(0), 'resources-total': z .number({ - invalid_type_error: errorMessage('count'), + invalid_type_error: SCI_EMBODIED_ERROR('count'), }) .gte(0) .min(0), }; const schemaWithVcpus = z.object({ - ...commonSchemaPart(errorMessage), + ...commonSchemaPart(SCI_EMBODIED_ERROR), ...vcpusSchemaPart, }); const schemaWithResources = z.object({ - ...commonSchemaPart(errorMessage), + ...commonSchemaPart(SCI_EMBODIED_ERROR), ...resourcesSchemaPart, }); From 0d978f48bd07c05fe8a8bd29628d22731f386437 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:00 +0400 Subject: [PATCH 16/38] feat(builtins): update errors usage in sci --- src/builtins/sci/index.ts | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/builtins/sci/index.ts b/src/builtins/sci/index.ts index 7a60db439..a39e33c94 100644 --- a/src/builtins/sci/index.ts +++ b/src/builtins/sci/index.ts @@ -1,16 +1,17 @@ import {z} from 'zod'; -import {ExecutePlugin, PluginParams} from '../../types/interface'; -import {ConfigParams} from '../../types/common'; - import {validate, allDefined} from '../../util/validations'; -import {buildErrorMessage} from '../../util/helpers'; import {ERRORS} from '../../util/errors'; -const {InputValidationError} = ERRORS; +import {STRINGS} from '../../config'; + +import {ExecutePlugin, PluginParams} from '../../types/interface'; +import {ConfigParams} from '../../types/common'; + +const {MissingInputDataError} = ERRORS; +const {MISSING_FUNCTIONAL_UNIT_CONFIG, MISSING_FUNCTIONAL_UNIT_INPUT} = STRINGS; export const Sci = (globalConfig: ConfigParams): ExecutePlugin => { - const errorBuilder = buildErrorMessage(Sci.name); const metadata = { kind: 'execute', }; @@ -19,15 +20,12 @@ export const Sci = (globalConfig: ConfigParams): ExecutePlugin => { * Validates node and gloabl configs. */ const validateConfig = (config?: ConfigParams) => { - const errorMessage = - '`functional-unit` should be provided in your global config'; - const schema = z .object({ 'functional-unit': z.string(), }) .refine(data => data['functional-unit'], { - message: errorMessage, + message: MISSING_FUNCTIONAL_UNIT_CONFIG, }); return validate>(schema, config); @@ -36,19 +34,19 @@ export const Sci = (globalConfig: ConfigParams): ExecutePlugin => { /** * Calculate the total emissions for a list of inputs. */ - const execute = (inputs: PluginParams[]): PluginParams[] => { - return inputs.map(input => { + const execute = (inputs: PluginParams[]): PluginParams[] => + inputs.map(input => { const safeInput = validateInput(input); const sci = safeInput['carbon'] > 0 ? safeInput['carbon'] / input[globalConfig['functional-unit']] : 0; + return { ...input, sci, }; }); - }; /** * Checks for fields in input. @@ -64,12 +62,7 @@ export const Sci = (globalConfig: ConfigParams): ExecutePlugin => { input[validatedConfig['functional-unit']] > 0 ) ) { - throw new InputValidationError( - errorBuilder({ - message: - 'functional-unit value is missing from input data or it is not a positive integer', - }) - ); + throw new MissingInputDataError(MISSING_FUNCTIONAL_UNIT_INPUT); } const schema = z From 296734aca0ca72addea6a4acdcf3f6bb40d12fd5 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:06 +0400 Subject: [PATCH 17/38] feat(builtins): update errors usage in regex --- src/builtins/regex/index.ts | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/builtins/regex/index.ts b/src/builtins/regex/index.ts index 7dc854461..5730697cc 100644 --- a/src/builtins/regex/index.ts +++ b/src/builtins/regex/index.ts @@ -1,16 +1,17 @@ import {z} from 'zod'; -import {buildErrorMessage} from '../../util/helpers'; import {ERRORS} from '../../util/errors'; import {validate} from '../../util/validations'; +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams} from '../../types/interface'; import {ConfigParams} from '../../types/common'; -const {InputValidationError, ConfigValidationError} = ERRORS; +const {MissingInputDataError, GlobalConfigError, RegexMismatchError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_INPUT_DATA, REGEX_MISMATCH} = STRINGS; export const Regex = (globalConfig: ConfigParams): ExecutePlugin => { - const errorBuilder = buildErrorMessage(Regex.name); const metadata = { kind: 'execute', }; @@ -20,10 +21,9 @@ export const Regex = (globalConfig: ConfigParams): ExecutePlugin => { */ const validateGlobalConfig = () => { if (!globalConfig) { - throw new ConfigValidationError( - errorBuilder({message: 'Configuration data is missing'}) - ); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } + const schema = z.object({ parameter: z.string().min(1), match: z.string().min(1), @@ -38,11 +38,7 @@ export const Regex = (globalConfig: ConfigParams): ExecutePlugin => { */ const validateSingleInput = (input: PluginParams, parameter: string) => { if (!input[parameter]) { - throw new InputValidationError( - errorBuilder({ - message: `\`${parameter}\` is missing from the input`, - }) - ); + throw new MissingInputDataError(MISSING_INPUT_DATA(parameter)); } return input; @@ -89,11 +85,7 @@ export const Regex = (globalConfig: ConfigParams): ExecutePlugin => { const matchedItem = input[parameter].match(regex); if (!matchedItem || !matchedItem[0]) { - throw new InputValidationError( - errorBuilder({ - message: `\`${input[parameter]}\` does not match the ${match} regex expression`, - }) - ); + throw new RegexMismatchError(REGEX_MISMATCH(input[parameter], match)); } return matchedItem[0]; From c60a1ab3504222b100c734c041168f11fa53e8fb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:13 +0400 Subject: [PATCH 18/38] feat(builtins): update errors usage in multiply --- src/builtins/multiply/index.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/builtins/multiply/index.ts b/src/builtins/multiply/index.ts index 5666a6093..ffcf4d14c 100644 --- a/src/builtins/multiply/index.ts +++ b/src/builtins/multiply/index.ts @@ -1,16 +1,17 @@ import {z} from 'zod'; -import {buildErrorMessage} from '../../util/helpers'; import {ERRORS} from '../../util/errors'; import {validate} from '../../util/validations'; +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams} from '../../types/interface'; import {MultiplyConfig} from './types'; -const {InputValidationError} = ERRORS; +const {MissingInputDataError} = ERRORS; +const {MISSING_INPUT_DATA} = STRINGS; export const Multiply = (globalConfig: MultiplyConfig): ExecutePlugin => { - const errorBuilder = buildErrorMessage(Multiply.name); const metadata = { kind: 'execute', }; @@ -42,11 +43,7 @@ export const Multiply = (globalConfig: MultiplyConfig): ExecutePlugin => { input[metricToMultiply] === undefined || isNaN(input[metricToMultiply]) ) { - throw new InputValidationError( - errorBuilder({ - message: `${metricToMultiply} is missing from the input array`, - }) - ); + throw new MissingInputDataError(MISSING_INPUT_DATA(metricToMultiply)); } }); From 58bdc8e45539d4ebcd8377c7a57c0ccf243a8068 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:20 +0400 Subject: [PATCH 19/38] feat(builtins): update errors usage in interpolation --- src/builtins/interpolation/index.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/builtins/interpolation/index.ts b/src/builtins/interpolation/index.ts index 3c0e0381b..35f88788b 100644 --- a/src/builtins/interpolation/index.ts +++ b/src/builtins/interpolation/index.ts @@ -6,9 +6,17 @@ import {ExecutePlugin, PluginParams, ConfigParams} from '../../types/interface'; import {validate} from '../../util/validations'; import {ERRORS} from '../../util/errors'; +import {STRINGS} from '../../config'; + import {Method} from './types'; -const {ConfigNotFoundError} = ERRORS; +const {GlobalConfigError} = ERRORS; +const { + MISSING_GLOBAL_CONFIG, + X_Y_EQUAL, + ARRAY_LENGTH_NON_EMPTY, + WITHIN_THE_RANGE, +} = STRINGS; export const Interpolation = (globalConfig: ConfigParams): ExecutePlugin => { /** @@ -118,7 +126,7 @@ export const Interpolation = (globalConfig: ConfigParams): ExecutePlugin => { */ const validateConfig = () => { if (!globalConfig) { - throw new ConfigNotFoundError('Global config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const schema = z @@ -130,10 +138,10 @@ export const Interpolation = (globalConfig: ConfigParams): ExecutePlugin => { 'output-parameter': z.string(), }) .refine(data => data.x && data.y && data.x.length === data.y.length, { - message: 'The length of `x` and `y` should be equal', + message: X_Y_EQUAL, }) .refine(data => data.x.length > 1 && data.y.length > 1, { - message: 'the length of the input arrays must be greater than 1', + message: ARRAY_LENGTH_NON_EMPTY, }); const defaultMethod = globalConfig.method ?? Method.LINEAR; @@ -171,8 +179,7 @@ export const Interpolation = (globalConfig: ConfigParams): ExecutePlugin => { data[inputParameter] >= globalConfig.x[0] && data[inputParameter] <= globalConfig.x[globalConfig.x.length - 1], { - message: - 'The target x value must be within the range of the given x values', + message: WITHIN_THE_RANGE, } ); From b11aa7e1566de4e63ff2bfc5347c0e8d428dd6f1 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:26 +0400 Subject: [PATCH 20/38] feat(builtins): update errors usage in exponent --- src/builtins/exponent/index.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/builtins/exponent/index.ts b/src/builtins/exponent/index.ts index 404161e63..0de914fda 100644 --- a/src/builtins/exponent/index.ts +++ b/src/builtins/exponent/index.ts @@ -1,16 +1,17 @@ import {z} from 'zod'; import {ERRORS} from '../../util/errors'; -import {buildErrorMessage} from '../../util/helpers'; import {validate} from '../../util/validations'; +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams} from '../../types/interface'; import {ExponentConfig} from './types'; -const {InputValidationError} = ERRORS; +const {MissingInputDataError, InputValidationError} = ERRORS; +const {MISSING_INPUT_DATA, NOT_NUMERIC_VALUE} = STRINGS; export const Exponent = (globalConfig: ExponentConfig): ExecutePlugin => { - const errorBuilder = buildErrorMessage(Exponent.name); const metadata = { kind: 'execute', }; @@ -41,21 +42,13 @@ export const Exponent = (globalConfig: ExponentConfig): ExecutePlugin => { const validateParamExists = (input: PluginParams, param: string) => { if (input[param] === undefined) { - throw new InputValidationError( - errorBuilder({ - message: `${param} is missing from the input array`, - }) - ); + throw new MissingInputDataError(MISSING_INPUT_DATA(param)); } }; const validateNumericString = (str: string) => { if (isNaN(+Number(str))) { - throw new InputValidationError( - errorBuilder({ - message: `${str} is not numeric`, - }) - ); + throw new InputValidationError(NOT_NUMERIC_VALUE(str)); } }; From 2e2b07fc252f773f52f5fd3d9335489d08afad6f Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:32 +0400 Subject: [PATCH 21/38] feat(builtins): update errors usage in divide --- src/builtins/divide/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/builtins/divide/index.ts b/src/builtins/divide/index.ts index b91d4c9fa..e50ca81f9 100644 --- a/src/builtins/divide/index.ts +++ b/src/builtins/divide/index.ts @@ -3,9 +3,12 @@ import {z} from 'zod'; import {ERRORS} from '../../util/errors'; import {validate} from '../../util/validations'; +import {STRINGS} from '../../config'; + import {ExecutePlugin, PluginParams, ConfigParams} from '../../types/interface'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +const {GlobalConfigError, MissingInputDataError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_INPUT_DATA} = STRINGS; export const Divide = (globalConfig: ConfigParams): ExecutePlugin => { const metadata = { @@ -38,7 +41,7 @@ export const Divide = (globalConfig: ConfigParams): ExecutePlugin => { */ const validateGlobalConfig = () => { if (!globalConfig) { - throw new ConfigNotFoundError('Global config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const schema = z.object({ @@ -65,9 +68,7 @@ export const Divide = (globalConfig: ConfigParams): ExecutePlugin => { }) .refine(() => { if (typeof denominator === 'string' && !input[denominator]) { - throw new InputValidationError( - `\`${denominator}\` is missing from the input.` - ); + throw new MissingInputDataError(MISSING_INPUT_DATA(denominator)); } return true; }); From c16f46c36a4e8ad258bfff11cc3256955552167d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:56:52 +0400 Subject: [PATCH 22/38] feat(builtins): update errors usage in csv lookup, optimize strategy --- src/builtins/csv-lookup/index.ts | 106 +++++++++++++++++++------------ 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/src/builtins/csv-lookup/index.ts b/src/builtins/csv-lookup/index.ts index 2539a14cf..4f7df52be 100644 --- a/src/builtins/csv-lookup/index.ts +++ b/src/builtins/csv-lookup/index.ts @@ -5,12 +5,29 @@ import axios from 'axios'; import {z} from 'zod'; import {parse} from 'csv-parse/sync'; -import {ExecutePlugin, PluginParams} from '../../types/interface'; - import {validate} from '../../util/validations'; import {ERRORS} from '../../util/errors'; -const {ConfigNotFoundError, FileNotFoundError, InputValidationError} = ERRORS; +import {STRINGS} from '../../config'; + +import {ExecutePlugin, PluginParams} from '../../types/interface'; + +const { + FILE_FETCH_FAILED, + FILE_READ_FAILED, + MISSING_CSV_COLUMN, + NO_QUERY_DATA, + MISSING_GLOBAL_CONFIG, +} = STRINGS; + +const { + FetchingFileError, + ReadFileError, + MissingCSVColumnError, + QueryDataNotFoundError, + GlobalConfigError, + CSVParseError, +} = ERRORS; export const CSVLookup = (globalConfig: any): ExecutePlugin => { const metadata = { @@ -36,18 +53,16 @@ export const CSVLookup = (globalConfig: any): ExecutePlugin => { const retrieveFile = async (filepath: string) => { if (isURL(filepath)) { const {data} = await axios.get(filepath).catch(error => { - throw new FileNotFoundError(`Something went wrong while reading the file: ${filepath}. - ${error.response.message}`); + throw new FetchingFileError( + FILE_FETCH_FAILED(filepath, error.response.message) + ); }); return data; } return readFile(filepath).catch(error => { - throw new FileNotFoundError( - `Something went wrong while reading the file: ${filepath}. -${error}` - ); + throw new ReadFileError(FILE_READ_FAILED(filepath, error)); }); }; @@ -81,7 +96,7 @@ ${error}` */ const fieldAccessor = (field: string, object: any) => { if (!(`${field}` in object)) { - throw new InputValidationError(`There is no column with name: ${field}.`); + throw new MissingCSVColumnError(MISSING_CSV_COLUMN(field)); } return nanifyEmptyValues(object[field]); @@ -149,6 +164,24 @@ ${error}` return ifMatchesCriteria.every(value => value === true); }; + /** + * Parses CSV file. + */ + const parseCSVFile = (file: string | Buffer) => { + try { + const parsedCSV: any[] = parse(file, { + columns: true, + skip_empty_lines: true, + cast: true, + }); + + return parsedCSV; + } catch (error: any) { + console.error(error); + throw new CSVParseError(error); + } + }; + /** * 1. Validates global config. * 2. Tries to retrieve given file (with url or local path). @@ -160,41 +193,30 @@ ${error}` const {filepath, query, output} = safeGlobalConfig; const file = await retrieveFile(filepath); - - try { - const parsedCSV: any[] = parse(file, { - columns: true, - skip_empty_lines: true, - cast: true, + console.log(file); + const parsedCSV = parseCSVFile(file); + + return inputs.map(input => { + /** Collects query values from input. */ + const queryData: any = {}; + const queryKeys = Object.keys(query); + queryKeys.forEach(queryKey => { + const queryValue = query[queryKey]; + queryData[queryKey] = input[queryValue]; }); - return inputs.map(input => { - /** Collects query values from input. */ - const queryData: any = {}; - const queryKeys = Object.keys(query); - queryKeys.forEach(queryKey => { - const queryValue = query[queryKey]; - queryData[queryKey] = input[queryValue]; - }); - - /** Gets related data from CSV. */ - const relatedData = parsedCSV.find(withCriteria(queryData)); + /** Gets related data from CSV. */ + const relatedData = parsedCSV.find(withCriteria(queryData)); - if (!relatedData) { - throw new InputValidationError( - 'One or more of the given query parameters are not found in the target CSV file column headers.' - ); - } + if (!relatedData) { + throw new QueryDataNotFoundError(NO_QUERY_DATA); + } - return { - ...input, - ...filterOutput(relatedData, {output, query}), - }; - }); - } catch (error) { - throw new InputValidationError(`Error happened while parsing given CSV file: ${filepath} -${error}`); - } + return { + ...input, + ...filterOutput(relatedData, {output, query}), + }; + }); }; /** @@ -202,7 +224,7 @@ ${error}`); */ const validateGlobalConfig = () => { if (!globalConfig) { - throw new ConfigNotFoundError('Global config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const globalConfigSchema = z.object({ From 9b3ea3ee924677649d3548307f452dbab7ac0d6f Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:57:03 +0400 Subject: [PATCH 23/38] feat(builtins): update errors usage coefficient --- src/builtins/coefficient/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/builtins/coefficient/index.ts b/src/builtins/coefficient/index.ts index 627e5f64e..c049911ae 100644 --- a/src/builtins/coefficient/index.ts +++ b/src/builtins/coefficient/index.ts @@ -5,9 +5,12 @@ import {ExecutePlugin, PluginParams} from '../../types/interface'; import {validate} from '../../util/validations'; import {ERRORS} from '../../util/errors'; +import {STRINGS} from '../../config'; + import {CoefficientConfig} from './types'; -const {ConfigNotFoundError} = ERRORS; +const {GlobalConfigError} = ERRORS; +const {MISSING_GLOBAL_CONFIG} = STRINGS; export const Coefficient = (globalConfig: CoefficientConfig): ExecutePlugin => { const metadata = { @@ -45,7 +48,7 @@ export const Coefficient = (globalConfig: CoefficientConfig): ExecutePlugin => { */ const validateGlobalConfig = () => { if (!globalConfig) { - throw new ConfigNotFoundError('Global config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const globalConfigSchema = z.object({ From f088869822856a27c4873dbe1ccbb3b49a4fa738 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:57:37 +0400 Subject: [PATCH 24/38] feat(builtins): update errors usage time-sync --- src/builtins/time-sync.ts | 57 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/builtins/time-sync.ts b/src/builtins/time-sync.ts index 67d5c9079..469fdec35 100644 --- a/src/builtins/time-sync.ts +++ b/src/builtins/time-sync.ts @@ -5,6 +5,7 @@ import {z} from 'zod'; import {parameterize} from '../lib/parameterize'; import {ERRORS} from '../util/errors'; +import {validate} from '../util/validations'; import {STRINGS} from '../config'; @@ -14,16 +15,24 @@ import { TimeNormalizerConfig, TimeParams, } from '../types/time-sync'; -import {validate} from '../util/validations'; Settings.defaultZone = 'utc'; -const {InputValidationError} = ERRORS; +const { + GlobalConfigError, + InvalidDateInInputError, + InvalidPaddingError, + InvalidInputError, +} = ERRORS; const { INVALID_TIME_NORMALIZATION, INVALID_OBSERVATION_OVERLAP, AVOIDING_PADDING_BY_EDGES, + INVALID_DATE_TYPE, + START_LOWER_END, + TIMESTAMP_REQUIRED, + INVALID_DATETIME, } = STRINGS; export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { @@ -64,7 +73,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { seconds: previousInput.duration, }) > currentMoment ) { - throw new InputValidationError(INVALID_OBSERVATION_OVERLAP); + throw new InvalidInputError(INVALID_OBSERVATION_OVERLAP); } const compareableTime = previousInputTimestamp.plus({ @@ -106,19 +115,22 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { }; const parseDate = (date: Date | string) => { - if (!date) return DateTime.invalid('Invalid date'); + if (!date) { + return DateTime.invalid('Invalid date'); + } + // dates are passed to time-sync.ts both in ISO 8601 format // and as a Date object (from the deserialization of a YAML file) // if the YAML parser fails to identify as a date, it passes as a string if (isDate(date)) { return DateTime.fromJSDate(date); } + if (typeof date === 'string') { return DateTime.fromISO(date); } - throw new InputValidationError( - `Unexpected date datatype: ${typeof date}: ${date}` - ); + + throw new InvalidDateInInputError(INVALID_DATE_TYPE(date)); }; /** @@ -128,10 +140,10 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { const schema = z.object({ timestamp: z .string({ - required_error: `required in input[${index}]`, + required_error: TIMESTAMP_REQUIRED(index), }) .datetime({ - message: `invalid datetime in input[${index}]`, + message: INVALID_DATETIME(index), }) .or(z.date()), duration: z.number(), @@ -145,7 +157,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { */ const validateGlobalConfig = () => { if (globalConfig === undefined) { - throw new InputValidationError(INVALID_TIME_NORMALIZATION); + throw new GlobalConfigError(INVALID_TIME_NORMALIZATION); } const schema = z @@ -156,7 +168,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { 'allow-padding': z.boolean(), }) .refine(data => data['start-time'] < data['end-time'], { - message: '`start-time` should be lower than `end-time`', + message: START_LOWER_END, }); return validate>(schema, globalConfig); @@ -176,8 +188,10 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { i: number ) => { const thisMoment = parseDate(currentRoundMoment).startOf('second'); + return thisMoment.plus({seconds: i}); }; + /** * Breaks down input per minimal time unit. */ @@ -259,8 +273,9 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { const validatePadding = (pad: PaddingReceipt, params: TimeParams): void => { const {start, end} = pad; const isPaddingNeeded = start || end; + if (!params.allowPadding && isPaddingNeeded) { - throw new InputValidationError(AVOIDING_PADDING_BY_EDGES(start, end)); + throw new InvalidPaddingError(AVOIDING_PADDING_BY_EDGES(start, end)); } }; @@ -292,8 +307,8 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { * Iterates over given inputs frame, meanwhile checking if aggregation method is `sum`, then calculates it. * For methods is `avg` and `none` calculating average of the frame. */ - const resampleInputFrame = (inputsInTimeslot: PluginParams[]) => { - return inputsInTimeslot.reduce((acc, input, index, inputs) => { + const resampleInputFrame = (inputsInTimeslot: PluginParams[]) => + inputsInTimeslot.reduce((acc, input, index, inputs) => { const metrics = Object.keys(input); metrics.forEach(metric => { @@ -336,13 +351,12 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { return acc; }, {} as PluginParams); - }; /** * Takes each array frame with interval length, then aggregating them together as from units.yaml file. */ - const resampleInputs = (inputs: PluginParams[], params: TimeParams) => { - return inputs.reduce((acc: PluginParams[], _input, index, inputs) => { + const resampleInputs = (inputs: PluginParams[], params: TimeParams) => + inputs.reduce((acc: PluginParams[], _input, index, inputs) => { const frameStart = index * params.interval; const frameEnd = (index + 1) * params.interval; const inputsFrame = inputs.slice(frameStart, frameEnd); @@ -356,7 +370,6 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { return acc; }, [] as PluginParams[]); - }; /** * Pads zeroish inputs from the beginning or at the end of the inputs if needed. @@ -394,6 +407,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { ) ); } + return paddedArray; }; @@ -404,6 +418,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { ) => { const array: PluginParams[] = []; const dateRange = Interval.fromDateTimes(startDate, endDate); + for (const interval of dateRange.splitBy({second: 1})) { array.push( fillWithZeroishInput( @@ -415,6 +430,7 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { ) ); } + return array; }; @@ -424,8 +440,8 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { const trimInputsByGlobalTimeline = ( inputs: PluginParams[], params: TimeParams - ): PluginParams[] => { - return inputs.reduce((acc: PluginParams[], item) => { + ): PluginParams[] => + inputs.reduce((acc: PluginParams[], item) => { const {timestamp} = item; if ( @@ -437,7 +453,6 @@ export const TimeSync = (globalConfig: TimeNormalizerConfig): ExecutePlugin => { return acc; }, [] as PluginParams[]); - }; return {metadata, execute}; }; From b34df91e030b646e99e7a41fe9bc2f072a3bf233 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:57:52 +0400 Subject: [PATCH 25/38] feat(builtins): update errors usage group-by --- src/builtins/group-by.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builtins/group-by.ts b/src/builtins/group-by.ts index 6a7036adf..e4e8af958 100644 --- a/src/builtins/group-by.ts +++ b/src/builtins/group-by.ts @@ -8,9 +8,9 @@ import {GroupByConfig} from '../types/group-by'; import {ERRORS} from '../util/errors'; import {validate} from '../util/validations'; -const {InvalidGroupingError, InputValidationError} = ERRORS; +const {InvalidGroupingError, GlobalConfigError} = ERRORS; -const {INVALID_GROUP_BY} = STRINGS; +const {INVALID_GROUP_BY, MISSING_GLOBAL_CONFIG} = STRINGS; /** * Plugin for inputs grouping. @@ -79,7 +79,7 @@ export const GroupBy = (): GroupByPlugin => { */ const validateConfig = (config: GroupByConfig) => { if (!config) { - throw new InputValidationError('Config is not provided.'); + throw new GlobalConfigError(MISSING_GLOBAL_CONFIG); } const schema = z.object({ From 93adf7146ada5be7c7af1b982edc1c9450c9afbb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:58:03 +0400 Subject: [PATCH 26/38] feat(builtins): update errors usage export yaml --- src/builtins/export-yaml.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/builtins/export-yaml.ts b/src/builtins/export-yaml.ts index e242bbcc2..d4f71c3f5 100644 --- a/src/builtins/export-yaml.ts +++ b/src/builtins/export-yaml.ts @@ -1,9 +1,12 @@ import {saveYamlFileAs} from '../util/yaml'; import {ERRORS} from '../util/errors'; +import {STRINGS} from '../config'; + import {Context} from '../types/manifest'; -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError} = ERRORS; +const {OUTPUT_REQUIRED} = STRINGS; export const ExportYaml = () => { /** Takes string before hashtag. */ @@ -14,7 +17,7 @@ export const ExportYaml = () => { */ const execute = async (tree: any, context: Context, outputPath: string) => { if (!outputPath) { - throw new ExhaustError('Output path is required.'); + throw new ExhaustOutputArgError(OUTPUT_REQUIRED); } const outputFile = { From b6d9b20e570d234572eaa6819acd6a35e9ddbc61 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:58:08 +0400 Subject: [PATCH 27/38] feat(builtins): update errors usage export csv --- src/builtins/export-csv.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/builtins/export-csv.ts b/src/builtins/export-csv.ts index efd2cc3cb..b0f4f80e8 100644 --- a/src/builtins/export-csv.ts +++ b/src/builtins/export-csv.ts @@ -3,10 +3,13 @@ import {stringify} from 'csv-stringify/sync'; import {ERRORS} from '../util/errors'; +import {STRINGS} from '../config'; + import {Context} from '../types/manifest'; import {PluginParams} from '../types/interface'; -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError} = ERRORS; +const {CSV_EXPORT, OUTPUT_REQUIRED} = STRINGS; /** * Extension to IF that outputs the tree in a CSV format. @@ -16,15 +19,14 @@ export const ExportCSV = () => { const validatedPath = validateOutputPath(outputPath); const paths = validatedPath.split('#'); - const output = paths.slice(0, paths.length - 1).join(''); const criteria = paths[paths.length - 1]; if (paths.length <= 1 || !criteria) { - throw new ExhaustError( - 'CSV export criteria is not found in output path. Please append it after --output #.' - ); + throw new ExhaustOutputArgError(CSV_EXPORT); } + const output = paths.slice(0, paths.length - 1).join(''); + return { output, criteria, @@ -36,11 +38,7 @@ export const ExportCSV = () => { */ const validateOutputPath = (outputPath: string) => { if (!outputPath) { - throw new ExhaustError('Output path is required.'); - } - - if (!outputPath.includes('#')) { - throw new ExhaustError('Output path should contain `#`.'); + throw new ExhaustOutputArgError(OUTPUT_REQUIRED); } return outputPath; From 19f1eeccb9585d698b2f6c769a19816676dbb489 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:58:14 +0400 Subject: [PATCH 28/38] feat(builtins): update errors usage export csv raw --- src/builtins/export-csv-raw.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/builtins/export-csv-raw.ts b/src/builtins/export-csv-raw.ts index 429d99c85..429990902 100644 --- a/src/builtins/export-csv-raw.ts +++ b/src/builtins/export-csv-raw.ts @@ -2,10 +2,13 @@ import * as fs from 'fs/promises'; import {ERRORS} from '../util/errors'; +import {STRINGS} from '../config'; + import {ExhaustPluginInterface} from '../types/exhaust-plugin-interface'; import {Context} from '../types/manifest'; -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError, WriteFileError} = ERRORS; +const {OUTPUT_REQUIRED, WRITE_CSV_ERROR} = STRINGS; export const ExportCSVRaw = (): ExhaustPluginInterface => { /** @@ -127,7 +130,7 @@ export const ExportCSVRaw = (): ExhaustPluginInterface => { try { await fs.writeFile(`${outputPath}.csv`, content); } catch (error) { - throw new ExhaustError(`Failed to write CSV to ${outputPath}: ${error}`); + throw new WriteFileError(WRITE_CSV_ERROR(outputPath, error)); } }; @@ -136,7 +139,7 @@ export const ExportCSVRaw = (): ExhaustPluginInterface => { */ const execute = async (tree: any, _context: Context, outputPath: string) => { if (!outputPath) { - throw new ExhaustError('Output path is required.'); + throw new ExhaustOutputArgError(OUTPUT_REQUIRED); } const [extractredFlatMap, extractedHeaders] = From cfc89dbec2bad8aaa656faaf1d62114b3edf89ef Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:58:45 +0400 Subject: [PATCH 29/38] test(util): migrate tests to use new errors --- src/__tests__/unit/util/aggregation-helper.test.ts | 11 ++++++----- src/__tests__/unit/util/args.test.ts | 10 +++++----- src/__tests__/unit/util/plugin-storage.test.ts | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/__tests__/unit/util/aggregation-helper.test.ts b/src/__tests__/unit/util/aggregation-helper.test.ts index 20690bc2f..7cccbfccc 100644 --- a/src/__tests__/unit/util/aggregation-helper.test.ts +++ b/src/__tests__/unit/util/aggregation-helper.test.ts @@ -5,7 +5,7 @@ import {STRINGS} from '../../../config'; import {PluginParams} from '../../../types/interface'; -const {InvalidAggregationParamsError} = ERRORS; +const {InvalidAggregationMethodError, MissingAggregationParamError} = ERRORS; const {INVALID_AGGREGATION_METHOD, METRIC_MISSING} = STRINGS; describe('util/aggregation-helper: ', () => { @@ -20,9 +20,9 @@ describe('util/aggregation-helper: ', () => { try { aggregateInputsIntoOne(inputs, metrics, isTemporal); } catch (error) { - expect(error).toBeInstanceOf(InvalidAggregationParamsError); + expect(error).toBeInstanceOf(InvalidAggregationMethodError); - if (error instanceof InvalidAggregationParamsError) { + if (error instanceof InvalidAggregationMethodError) { expect(error.message).toEqual(INVALID_AGGREGATION_METHOD(metrics[0])); } } @@ -38,9 +38,10 @@ describe('util/aggregation-helper: ', () => { try { aggregateInputsIntoOne(inputs, metrics, isTemporal); } catch (error) { - expect(error).toBeInstanceOf(InvalidAggregationParamsError); + console.log(error); + expect(error).toBeInstanceOf(MissingAggregationParamError); - if (error instanceof InvalidAggregationParamsError) { + if (error instanceof MissingAggregationParamError) { expect(error.message).toEqual(METRIC_MISSING(metrics[0], 0)); } } diff --git a/src/__tests__/unit/util/args.test.ts b/src/__tests__/unit/util/args.test.ts index d40d36cae..7da9efa37 100644 --- a/src/__tests__/unit/util/args.test.ts +++ b/src/__tests__/unit/util/args.test.ts @@ -76,7 +76,7 @@ import {ERRORS} from '../../../util/errors'; import {STRINGS} from '../../../config'; -const {CliInputError} = ERRORS; +const {CliInputError, CliSourceFileError} = ERRORS; const { MANIFEST_IS_MISSING, @@ -115,8 +115,8 @@ describe('util/args: ', () => { try { parseIEProcessArgs(); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); - expect(error).toEqual(new CliInputError(MANIFEST_IS_MISSING)); + expect(error).toBeInstanceOf(CliSourceFileError); + expect(error).toEqual(new CliSourceFileError(MANIFEST_IS_MISSING)); } }); @@ -195,8 +195,8 @@ describe('util/args: ', () => { try { parseIEProcessArgs(); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); - expect(error).toEqual(new CliInputError(FILE_IS_NOT_YAML)); + expect(error).toBeInstanceOf(CliSourceFileError); + expect(error).toEqual(new CliSourceFileError(FILE_IS_NOT_YAML)); } }); diff --git a/src/__tests__/unit/util/plugin-storage.test.ts b/src/__tests__/unit/util/plugin-storage.test.ts index b2bd25011..f8ac5b631 100644 --- a/src/__tests__/unit/util/plugin-storage.test.ts +++ b/src/__tests__/unit/util/plugin-storage.test.ts @@ -1,7 +1,7 @@ import {pluginStorage} from '../../../util/plugin-storage'; import {ERRORS} from '../../../util/errors'; -const {PluginInitalizationError} = ERRORS; +const {PluginInitializationError} = ERRORS; describe('util/pluginStorage: ', () => { describe('pluginStorage(): ', () => { @@ -30,9 +30,9 @@ describe('util/pluginStorage: ', () => { try { storage.get(pluginName); } catch (error) { - expect(error).toBeInstanceOf(PluginInitalizationError); + expect(error).toBeInstanceOf(PluginInitializationError); - if (error instanceof PluginInitalizationError) { + if (error instanceof PluginInitializationError) { expect(error.message).toEqual; } } From fe46a8d6542bddb9ec8419c57f881c652cba9958 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:58:56 +0400 Subject: [PATCH 30/38] test(lib): migrate tests to use new errors --- src/__tests__/unit/lib/exhaust.test.ts | 22 +++++++--------- src/__tests__/unit/lib/initialize.test.ts | 32 ++++++++++++++--------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/__tests__/unit/lib/exhaust.test.ts b/src/__tests__/unit/lib/exhaust.test.ts index 97db0b9dc..a1f46595a 100644 --- a/src/__tests__/unit/lib/exhaust.test.ts +++ b/src/__tests__/unit/lib/exhaust.test.ts @@ -7,8 +7,8 @@ import {ERRORS} from '../../../util/errors'; import {STRINGS} from '../../../config'; -const {ExhaustError} = ERRORS; -const {INVALID_EXHAUST_PLUGIN} = STRINGS; +const {ExhaustOutputArgError, InvalidExhaustPluginError} = ERRORS; +const {INVALID_EXHAUST_PLUGIN, OUTPUT_REQUIRED} = STRINGS; describe('lib/exhaust: ', () => { describe('exhaust(): ', () => { @@ -50,7 +50,6 @@ describe('lib/exhaust: ', () => { outputs: ['yaml'], }, }; - const expectedMessage = 'Output path is required.'; expect.assertions(2); @@ -58,10 +57,10 @@ describe('lib/exhaust: ', () => { // @ts-ignore await exhaust(tree, context, {}); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); + expect(error).toBeInstanceOf(ExhaustOutputArgError); - if (error instanceof ExhaustError) { - expect(error.message).toEqual(expectedMessage); + if (error instanceof ExhaustOutputArgError) { + expect(error.message).toEqual(OUTPUT_REQUIRED); } } }); @@ -73,9 +72,6 @@ describe('lib/exhaust: ', () => { outputs: ['mock'], }, }; - const expectedMessage = INVALID_EXHAUST_PLUGIN( - context.initialize.outputs[0] - ); expect.assertions(2); @@ -83,10 +79,12 @@ describe('lib/exhaust: ', () => { // @ts-ignore await exhaust(tree, context, {}); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); + expect(error).toBeInstanceOf(InvalidExhaustPluginError); - if (error instanceof ExhaustError) { - expect(error.message).toEqual(expectedMessage); + if (error instanceof InvalidExhaustPluginError) { + expect(error.message).toEqual( + INVALID_EXHAUST_PLUGIN(context.initialize.outputs[0]) + ); } } }); diff --git a/src/__tests__/unit/lib/initialize.test.ts b/src/__tests__/unit/lib/initialize.test.ts index ad5541684..23ca27171 100644 --- a/src/__tests__/unit/lib/initialize.test.ts +++ b/src/__tests__/unit/lib/initialize.test.ts @@ -18,7 +18,11 @@ import {STRINGS} from '../../../config'; import {GlobalPlugins} from '../../../types/manifest'; -const {PluginCredentialError, ModuleInitializationError} = ERRORS; +const { + MissingPluginPathError, + MissingPluginMethodError, + PluginInitializationError, +} = ERRORS; const {MISSING_METHOD, MISSING_PATH, INVALID_MODULE_PATH} = STRINGS; describe('lib/initalize: ', () => { @@ -81,9 +85,9 @@ describe('lib/initalize: ', () => { try { await initialize(plugins); } catch (error) { - expect(error).toBeInstanceOf(PluginCredentialError); + expect(error).toBeInstanceOf(MissingPluginPathError); - if (error instanceof PluginCredentialError) { + if (error instanceof MissingPluginPathError) { expect(error.message).toEqual(MISSING_PATH); } } @@ -103,9 +107,9 @@ describe('lib/initalize: ', () => { try { await initialize(plugins); } catch (error) { - expect(error).toBeInstanceOf(PluginCredentialError); + expect(error).toBeInstanceOf(MissingPluginMethodError); - if (error instanceof PluginCredentialError) { + if (error instanceof MissingPluginMethodError) { expect(error.message).toEqual(MISSING_METHOD); } } @@ -160,14 +164,16 @@ describe('lib/initalize: ', () => { try { await initialize(plugins); - } catch (error) { - expect(error).toBeInstanceOf(ModuleInitializationError); - - if (error instanceof ModuleInitializationError) { - expect(error.message).toEqual( - INVALID_MODULE_PATH(plugins.mockavizta.path) - ); - } + } catch (error: any) { + expect(error).toBeInstanceOf(PluginInitializationError); + expect(error.message).toEqual( + INVALID_MODULE_PATH( + plugins.mockavizta.path, + new Error( + "Cannot find module 'failing-mock' from 'src/lib/initialize.ts'" + ) + ) + ); } }); }); From 6cca843d563fb7c3241d4704d43321a5e781d831 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:59:14 +0400 Subject: [PATCH 31/38] test(config): drop strings tests --- src/__tests__/unit/config/strings.test.ts | 171 ---------------------- 1 file changed, 171 deletions(-) delete mode 100644 src/__tests__/unit/config/strings.test.ts diff --git a/src/__tests__/unit/config/strings.test.ts b/src/__tests__/unit/config/strings.test.ts deleted file mode 100644 index bdc1f871f..000000000 --- a/src/__tests__/unit/config/strings.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import {STRINGS} from '../../../config/strings'; - -const { - NOT_NATIVE_PLUGIN, - INVALID_MODULE_PATH, - INVALID_AGGREGATION_METHOD, - METRIC_MISSING, - AVOIDING_PADDING, - AVOIDING_PADDING_BY_EDGES, - INVALID_GROUP_BY, - REJECTING_OVERRIDE, - INVALID_EXHAUST_PLUGIN, - UNKNOWN_PARAM, - NOT_INITALIZED_PLUGIN, -} = STRINGS; - -describe('config/strings: ', () => { - describe('NOT_NATIVE_PLUGIN(): ', () => { - it('successfully injects path into message.', () => { - const path = 'mock/path'; - const expectedMessage = ` -You are using plugin ${path} which is not part of the Impact Framework standard library. You should do your own research to ensure the plugins are up to date and accurate. They may not be actively maintained.`; - - expect(NOT_NATIVE_PLUGIN(path)).toEqual(expectedMessage); - }); - }); - - describe('INVALID_MODULE_PATH(): ', () => { - it('successfully appends given param to message.', () => { - const param = 'mock-param'; - - const expectedMessage = `Provided module: '${param}' is invalid or not found.`; - - expect(INVALID_MODULE_PATH(param)).toEqual(expectedMessage); - }); - }); - - describe('AVOIDING_PADDING(): ', () => { - it('successfully appends given param to message.', () => { - const param = 'mock-param'; - - const expectedMessage = `Avoiding padding at ${param}`; - - expect(AVOIDING_PADDING(param)).toEqual(expectedMessage); - }); - }); - - describe('AVOIDING_PADDING_BY_EDGES(): ', () => { - it('successfully appends given start and end params.', () => { - const start = true; - const end = true; - - const expectedMessage = 'Avoiding padding at start and end'; - - expect(AVOIDING_PADDING_BY_EDGES(start, end)).toEqual(expectedMessage); - }); - - it('successfully appends given start param.', () => { - const start = true; - const end = false; - - const expectedMessage = 'Avoiding padding at start'; - - expect(AVOIDING_PADDING_BY_EDGES(start, end)).toEqual(expectedMessage); - }); - - it('successfully appends given end param.', () => { - const start = false; - const end = true; - - const expectedMessage = 'Avoiding padding at end'; - - expect(AVOIDING_PADDING_BY_EDGES(start, end)).toEqual(expectedMessage); - }); - }); - - describe('INVALID_AGGREGATION_METHOD(): ', () => { - it('successfully appends given param to message.', () => { - const param = 'mock-param'; - - const expectedMessage = `Aggregation is not possible for given ${param} since method is 'none'.`; - - expect(INVALID_AGGREGATION_METHOD(param)).toEqual(expectedMessage); - }); - }); - - describe('METRIC_MISSING(): ', () => { - it('successfully appends given param to message.', () => { - const metric = 'mock-metric'; - const index = 0; - - const expectedMessage = `Aggregation metric ${metric} is not found in inputs[${index}].`; - - expect(METRIC_MISSING(metric, index)).toEqual(expectedMessage); - }); - }); - - describe('INVALID_GROUP_BY(): ', () => { - it('injects type in given message.', () => { - const type = 'mock-type'; - const message = `Invalid group ${type}.`; - - expect(INVALID_GROUP_BY(type)).toEqual(message); - }); - }); - - describe('REJECTING_OVERRIDE(): ', () => { - it('inejcts param name into message.', () => { - const param: any = { - name: 'mock-name', - description: 'mock-description', - aggregation: 'sum', - unit: 'mock-unit', - }; - - expect(REJECTING_OVERRIDE(param)); - }); - }); - - describe('INVALID_EXHAUST_PLUGIN(): ', () => { - it('injects plugin name into message.', () => { - const pluginName = 'mock-plugin'; - const message = `Invalid exhaust plugin: ${pluginName}.`; - - expect(INVALID_EXHAUST_PLUGIN(pluginName)).toEqual(message); - }); - }); - - describe('AVOIDING_PADDING(): ', () => { - it('successfully appends given param to message.', () => { - const description_suffix = 'pad description'; - - const expectedMessage = `Avoiding padding at ${description_suffix}`; - - expect(AVOIDING_PADDING(description_suffix)).toEqual(expectedMessage); - }); - }); - - describe('UNKNOWN_PARAM(): ', () => { - it('injects name into message.', () => { - const name = 'mock-name'; - const message = `Unknown parameter: ${name}. Using 'sum' aggregation method.`; - - expect(UNKNOWN_PARAM(name)).toEqual(message); - }); - }); - - describe('NOT_INITALIZED_PLUGIN(): ', () => { - it('injects name into message.', () => { - const name = 'mock-name'; - const message = `Not initalized plugin: ${name}. Check if ${name} is in 'manifest.initalize.plugins'.`; - - expect(NOT_INITALIZED_PLUGIN(name)).toEqual(message); - }); - }); - - describe('AVOIDING_PADDING_BY_EDGES(): ', () => { - it('successfully combines boolean params into a description.', () => { - let description_suffix = 'start and end'; - let expectedMessage = `Avoiding padding at ${description_suffix}`; - - expect(AVOIDING_PADDING_BY_EDGES(true, true)).toEqual(expectedMessage); - description_suffix = 'start'; - expectedMessage = `Avoiding padding at ${description_suffix}`; - expect(AVOIDING_PADDING_BY_EDGES(true, false)).toEqual(expectedMessage); - description_suffix = 'end'; - expectedMessage = `Avoiding padding at ${description_suffix}`; - expect(AVOIDING_PADDING_BY_EDGES(false, true)).toEqual(expectedMessage); - }); - }); -}); From 55a9060ae3e0a065ebba2407246776ae40041a2d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 16:59:44 +0400 Subject: [PATCH 32/38] test(builtins): migrate tests to use updated error pattern --- .../unit/builtins/CommonGenerator.test.ts | 18 +++---- .../unit/builtins/RandIntGenerator.test.ts | 25 +++------ .../unit/builtins/coefficient.test.ts | 10 ++-- src/__tests__/unit/builtins/divide.test.ts | 17 +++--- src/__tests__/unit/builtins/exponent.test.ts | 30 +++++------ .../unit/builtins/export-csv-raw.test.ts | 18 ++++--- .../unit/builtins/export-csv.test.ts | 23 ++++---- .../unit/builtins/export-yaml.test.ts | 9 ++-- src/__tests__/unit/builtins/group-by.test.ts | 13 ++--- .../unit/builtins/interpolation.test.ts | 34 ++++++------ .../unit/builtins/mock-observations.test.ts | 14 ++--- src/__tests__/unit/builtins/multiply.test.ts | 11 ++-- src/__tests__/unit/builtins/regex.test.ts | 20 ++++--- .../unit/builtins/sci-embodied.test.ts | 25 ++++++--- src/__tests__/unit/builtins/sci.test.ts | 8 +-- src/__tests__/unit/builtins/shell.test.ts | 8 +-- src/__tests__/unit/builtins/subtract.test.ts | 9 ++-- src/__tests__/unit/builtins/sum.test.ts | 15 +++--- src/__tests__/unit/builtins/time-sync.test.ts | 52 ++++++++++++------- 19 files changed, 189 insertions(+), 170 deletions(-) diff --git a/src/__tests__/unit/builtins/CommonGenerator.test.ts b/src/__tests__/unit/builtins/CommonGenerator.test.ts index 881aa810e..d4d1bdac9 100644 --- a/src/__tests__/unit/builtins/CommonGenerator.test.ts +++ b/src/__tests__/unit/builtins/CommonGenerator.test.ts @@ -1,12 +1,12 @@ -import {KeyValuePair} from '../../../types/common'; - import {ERRORS} from '../../../util/errors'; import {CommonGenerator} from '../../../builtins/mock-observations/helpers/common-generator'; +import {STRINGS} from '../../../config'; -const {InputValidationError} = ERRORS; +const {GlobalConfigError} = ERRORS; +const {MISSING_GLOBAL_CONFIG} = STRINGS; -describe('lib/mock-observations/CommonGenerator: ', () => { +describe('builtins/mock-observations/CommonGenerator: ', () => { describe('initialize: ', () => { it('throws an error when config is not empty object.', async () => { const commonGenerator = CommonGenerator({}); @@ -16,18 +16,14 @@ describe('lib/mock-observations/CommonGenerator: ', () => { try { commonGenerator.next([]); } catch (error) { - expect(error).toEqual( - new InputValidationError( - 'CommonGenerator: Config must not be null or empty.' - ) - ); + expect(error).toEqual(new GlobalConfigError(MISSING_GLOBAL_CONFIG)); } }); }); describe('next(): ', () => { it('returns a result with valid data.', async () => { - const config: KeyValuePair = { + const config: Record = { key1: 'value1', key2: 'value2', }; @@ -35,7 +31,7 @@ describe('lib/mock-observations/CommonGenerator: ', () => { expect.assertions(1); - expect(commonGenerator.next([])).toStrictEqual({ + expect(commonGenerator.next([])).toEqual({ key1: 'value1', key2: 'value2', }); diff --git a/src/__tests__/unit/builtins/RandIntGenerator.test.ts b/src/__tests__/unit/builtins/RandIntGenerator.test.ts index c904f7350..1d2969695 100644 --- a/src/__tests__/unit/builtins/RandIntGenerator.test.ts +++ b/src/__tests__/unit/builtins/RandIntGenerator.test.ts @@ -4,20 +4,19 @@ import {ERRORS} from '../../../util/errors'; import {RandIntGenerator} from '../../../builtins/mock-observations/helpers/rand-int-generator'; -const {InputValidationError} = ERRORS; +import {STRINGS} from '../../../config'; -describe('lib/mock-observations/RandIntGenerator: ', () => { +const {GlobalConfigError} = ERRORS; +const {INVALID_NAME, MISSING_MIN_MAX, MISSING_GLOBAL_CONFIG} = STRINGS; + +describe('builtins/mock-observations/RandIntGenerator: ', () => { describe('initialize', () => { it('throws an error when the generator name is empty string.', async () => { expect.assertions(1); try { RandIntGenerator('', {}); } catch (error) { - expect(error).toEqual( - new InputValidationError( - 'RandIntGenerator: `name` is empty or all spaces.' - ) - ); + expect(error).toEqual(new GlobalConfigError(INVALID_NAME)); } }); @@ -26,11 +25,7 @@ describe('lib/mock-observations/RandIntGenerator: ', () => { try { RandIntGenerator('generator-name', {}); } catch (error) { - expect(error).toEqual( - new InputValidationError( - 'RandIntGenerator: Config must not be null or empty.' - ) - ); + expect(error).toEqual(new GlobalConfigError(MISSING_GLOBAL_CONFIG)); } }); @@ -42,11 +37,7 @@ describe('lib/mock-observations/RandIntGenerator: ', () => { try { RandIntGenerator('random', config); } catch (error) { - expect(error).toEqual( - new InputValidationError( - 'RandIntGenerator: Config is missing min or max.' - ) - ); + expect(error).toEqual(new GlobalConfigError(MISSING_MIN_MAX)); } }); }); diff --git a/src/__tests__/unit/builtins/coefficient.test.ts b/src/__tests__/unit/builtins/coefficient.test.ts index bf7455679..44595d774 100644 --- a/src/__tests__/unit/builtins/coefficient.test.ts +++ b/src/__tests__/unit/builtins/coefficient.test.ts @@ -2,7 +2,10 @@ import {Coefficient} from '../../../builtins/coefficient'; import {ERRORS} from '../../../util/errors'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +import {STRINGS} from '../../../config'; + +const {InputValidationError, GlobalConfigError} = ERRORS; +const {MISSING_GLOBAL_CONFIG} = STRINGS; describe('builtins/coefficient: ', () => { describe('Coefficient: ', () => { @@ -49,7 +52,6 @@ describe('builtins/coefficient: ', () => { it('throws an error when global config is not provided.', () => { const config = undefined; const coefficient = Coefficient(config!); - const expectedMessage = 'Global config is not provided.'; expect.assertions(1); @@ -62,7 +64,9 @@ describe('builtins/coefficient: ', () => { }, ]); } catch (error) { - expect(error).toStrictEqual(new ConfigNotFoundError(expectedMessage)); + expect(error).toStrictEqual( + new GlobalConfigError(MISSING_GLOBAL_CONFIG) + ); } }); diff --git a/src/__tests__/unit/builtins/divide.test.ts b/src/__tests__/unit/builtins/divide.test.ts index 8f56bb19d..9c794dae6 100644 --- a/src/__tests__/unit/builtins/divide.test.ts +++ b/src/__tests__/unit/builtins/divide.test.ts @@ -1,8 +1,10 @@ import {Divide} from '../../../builtins'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +const {InputValidationError, GlobalConfigError, MissingInputDataError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_INPUT_DATA} = STRINGS; describe('builtins/divide: ', () => { describe('Divide: ', () => { @@ -103,7 +105,6 @@ describe('builtins/divide: ', () => { }); it('throws an error on missing global config.', async () => { - const expectedMessage = 'Global config is not provided.'; const config = undefined; const divide = Divide(config!); @@ -117,7 +118,9 @@ describe('builtins/divide: ', () => { }, ]); } catch (error) { - expect(error).toStrictEqual(new ConfigNotFoundError(expectedMessage)); + expect(error).toStrictEqual( + new GlobalConfigError(MISSING_GLOBAL_CONFIG) + ); } }); @@ -148,8 +151,6 @@ describe('builtins/divide: ', () => { }); it('throws an error when `denominator` is string.', async () => { - const expectedMessage = '`10` is missing from the input.'; - const globalConfig = { numerator: 'vcpus-allocated', denominator: '10', @@ -168,7 +169,11 @@ describe('builtins/divide: ', () => { }, ]); } catch (error) { - expect(error).toStrictEqual(new InputValidationError(expectedMessage)); + expect(error).toStrictEqual( + new MissingInputDataError( + MISSING_INPUT_DATA(globalConfig.denominator) + ) + ); } }); }); diff --git a/src/__tests__/unit/builtins/exponent.test.ts b/src/__tests__/unit/builtins/exponent.test.ts index 54c69ffcb..6034fd3bd 100644 --- a/src/__tests__/unit/builtins/exponent.test.ts +++ b/src/__tests__/unit/builtins/exponent.test.ts @@ -1,10 +1,12 @@ import {Exponent} from '../../../builtins/exponent'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError} = ERRORS; +const {InputValidationError, MissingInputDataError} = ERRORS; +const {NOT_NUMERIC_VALUE, MISSING_INPUT_DATA} = STRINGS; -describe('lib/exponent: ', () => { +describe('builtins/exponent: ', () => { describe('Exponent: ', () => { const globalConfig = { 'input-parameter': 'energy/base', @@ -45,9 +47,6 @@ describe('lib/exponent: ', () => { }); it('throws an error on missing params in input.', async () => { - const expectedMessage = - 'Exponent: energy/base is missing from the input array.'; - expect.assertions(1); try { @@ -59,27 +58,26 @@ describe('lib/exponent: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new MissingInputDataError(MISSING_INPUT_DATA('energy/base')) ); } }); it('throws an error on input param value not numeric.', async () => { - const expectedMessage = 'Exponent: i-am-not-a-number is not numeric.'; - expect.assertions(1); + const input = [ + { + duration: 3600, + 'energy/base': 'i-am-not-a-number', + timestamp: '2021-01-01T00:00:00Z', + }, + ]; try { - await exponent.execute([ - { - duration: 3600, - 'energy/base': 'i-am-not-a-number', - timestamp: '2021-01-01T00:00:00Z', - }, - ]); + await exponent.execute(input); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new InputValidationError(NOT_NUMERIC_VALUE(input[0]['energy/base'])) ); } }); diff --git a/src/__tests__/unit/builtins/export-csv-raw.test.ts b/src/__tests__/unit/builtins/export-csv-raw.test.ts index 1ebf9e45e..a66a24ac2 100644 --- a/src/__tests__/unit/builtins/export-csv-raw.test.ts +++ b/src/__tests__/unit/builtins/export-csv-raw.test.ts @@ -4,9 +4,12 @@ import {jest} from '@jest/globals'; import {ExportCSVRaw} from '../../../builtins/export-csv-raw'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; + import {tree, context, outputs} from '../../../__mocks__/builtins/export-csv'; -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError} = ERRORS; +const {WRITE_CSV_ERROR, OUTPUT_REQUIRED} = STRINGS; jest.mock('fs/promises', () => ({ __esModule: true, @@ -40,18 +43,17 @@ describe('builtins/export-csv-raw: ', () => { it('throws an error when the CSV file could not be created.', async () => { const outputPath = 'output#carbon'; + const expectedMessage = 'Could not write CSV file.'; expect.assertions(1); - jest - .spyOn(fs, 'writeFile') - .mockRejectedValue('Could not write CSV file.'); + jest.spyOn(fs, 'writeFile').mockRejectedValue(expectedMessage); await expect( exportCSVRaw.execute(tree, context, outputPath) ).rejects.toThrow( - new ExhaustError( - 'Failed to write CSV to output#carbon: Could not write CSV file.' + new ExhaustOutputArgError( + WRITE_CSV_ERROR(outputPath, expectedMessage) ) ); }); @@ -65,8 +67,8 @@ describe('builtins/export-csv-raw: ', () => { try { await exportCSVRaw.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); - expect(error).toEqual(new ExhaustError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustOutputArgError); + expect(error).toEqual(new ExhaustOutputArgError(OUTPUT_REQUIRED)); } }); }); diff --git a/src/__tests__/unit/builtins/export-csv.test.ts b/src/__tests__/unit/builtins/export-csv.test.ts index 94414a970..09fd4e737 100644 --- a/src/__tests__/unit/builtins/export-csv.test.ts +++ b/src/__tests__/unit/builtins/export-csv.test.ts @@ -5,6 +5,8 @@ import {jest} from '@jest/globals'; import {ExportCSV} from '../../../builtins/export-csv'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; + import { tree, context, @@ -13,7 +15,8 @@ import { aggregation, } from '../../../__mocks__/builtins/export-csv'; -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError} = ERRORS; +const {OUTPUT_REQUIRED, CSV_EXPORT} = STRINGS; jest.mock('fs/promises', () => ({ writeFile: jest.fn<() => Promise>().mockResolvedValue(), @@ -197,8 +200,8 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); - expect(error).toEqual(new ExhaustError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustOutputArgError); + expect(error).toEqual(new ExhaustOutputArgError(OUTPUT_REQUIRED)); } }); @@ -210,10 +213,8 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); - expect(error).toEqual( - new ExhaustError('Output path should contain `#`.') - ); + expect(error).toBeInstanceOf(ExhaustOutputArgError); + expect(error).toEqual(new ExhaustOutputArgError(CSV_EXPORT)); } }); @@ -225,12 +226,8 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); - expect(error).toEqual( - new ExhaustError( - 'CSV export criteria is not found in output path. Please append it after --output #.' - ) - ); + expect(error).toBeInstanceOf(ExhaustOutputArgError); + expect(error).toEqual(new ExhaustOutputArgError(CSV_EXPORT)); } }); }); diff --git a/src/__tests__/unit/builtins/export-yaml.test.ts b/src/__tests__/unit/builtins/export-yaml.test.ts index a2157e4ef..f17b9154e 100644 --- a/src/__tests__/unit/builtins/export-yaml.test.ts +++ b/src/__tests__/unit/builtins/export-yaml.test.ts @@ -2,13 +2,16 @@ import {ExportYaml} from '../../../builtins/export-yaml'; import {ERRORS} from '../../../util/errors'; import {saveYamlFileAs} from '../../../util/yaml'; +import {STRINGS} from '../../../config'; + import {tree, context} from '../../../__mocks__/builtins/export-csv'; jest.mock('../../../util/yaml', () => ({ saveYamlFileAs: jest.fn(), })); -const {ExhaustError} = ERRORS; +const {ExhaustOutputArgError} = ERRORS; +const {OUTPUT_REQUIRED} = STRINGS; describe('builtins/export-yaml: ', () => { describe('ExportYaml: ', () => { @@ -38,8 +41,8 @@ describe('builtins/export-yaml: ', () => { try { await exportYaml.execute({}, context, ''); } catch (error) { - expect(error).toBeInstanceOf(ExhaustError); - expect(error).toEqual(new ExhaustError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustOutputArgError); + expect(error).toEqual(new ExhaustOutputArgError(OUTPUT_REQUIRED)); } }); }); diff --git a/src/__tests__/unit/builtins/group-by.test.ts b/src/__tests__/unit/builtins/group-by.test.ts index 9c99ee700..b472125d1 100644 --- a/src/__tests__/unit/builtins/group-by.test.ts +++ b/src/__tests__/unit/builtins/group-by.test.ts @@ -1,7 +1,10 @@ import {GroupBy} from '../../../builtins/group-by'; import {ERRORS} from '../../../util/errors'; -const {InvalidGroupingError, InputValidationError} = ERRORS; +import {STRINGS} from '../../../config'; + +const {InvalidGroupingError, InputValidationError, GlobalConfigError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, INVALID_GROUP_BY} = STRINGS; describe('builtins/group-by: ', () => { describe('GroupBy: ', () => { @@ -92,10 +95,8 @@ describe('builtins/group-by: ', () => { try { plugin.execute(inputs, config!); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); - expect(error).toEqual( - new InputValidationError('Config is not provided.') - ); + expect(error).toBeInstanceOf(GlobalConfigError); + expect(error).toEqual(new GlobalConfigError(MISSING_GLOBAL_CONFIG)); } }); @@ -166,7 +167,7 @@ describe('builtins/group-by: ', () => { } catch (error) { expect(error).toBeInstanceOf(InvalidGroupingError); expect(error).toEqual( - new InvalidGroupingError('Invalid group unknown.') + new InvalidGroupingError(INVALID_GROUP_BY(config.group[2])) ); } }); diff --git a/src/__tests__/unit/builtins/interpolation.test.ts b/src/__tests__/unit/builtins/interpolation.test.ts index c4bba939f..54dd2dab7 100644 --- a/src/__tests__/unit/builtins/interpolation.test.ts +++ b/src/__tests__/unit/builtins/interpolation.test.ts @@ -1,8 +1,16 @@ import {Interpolation} from '../../../builtins'; -import {Method} from '../../../builtins/interpolation/types'; import {ERRORS} from '../../../util/errors'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +import {Method} from '../../../builtins/interpolation/types'; +import {STRINGS} from '../../../config'; + +const {InputValidationError, GlobalConfigError} = ERRORS; +const { + MISSING_GLOBAL_CONFIG, + WITHIN_THE_RANGE, + ARRAY_LENGTH_NON_EMPTY, + X_Y_EQUAL, +} = STRINGS; describe('builtins/interpolation: ', () => { describe('Interpolation: ', () => { @@ -144,10 +152,8 @@ describe('builtins/interpolation: ', () => { try { plugin.execute(inputs); } catch (error) { - expect(error).toBeInstanceOf(ConfigNotFoundError); - expect(error).toEqual( - new ConfigNotFoundError('Global config is not provided.') - ); + expect(error).toBeInstanceOf(GlobalConfigError); + expect(error).toEqual(new GlobalConfigError(MISSING_GLOBAL_CONFIG)); } }); @@ -163,11 +169,7 @@ describe('builtins/interpolation: ', () => { plugin.execute(inputs); } catch (error) { expect(error).toBeInstanceOf(InputValidationError); - expect(error).toEqual( - new InputValidationError( - 'The length of `x` and `y` should be equal' - ) - ); + expect(error).toEqual(new InputValidationError(X_Y_EQUAL)); } }); @@ -184,11 +186,7 @@ describe('builtins/interpolation: ', () => { plugin.execute(inputs); } catch (error) { expect(error).toBeInstanceOf(InputValidationError); - expect(error).toEqual( - new InputValidationError( - 'The target x value must be within the range of the given x values' - ) - ); + expect(error).toEqual(new InputValidationError(WITHIN_THE_RANGE)); } }); it('throws an error when the the length of the input arrays is <2', () => { @@ -213,9 +211,7 @@ describe('builtins/interpolation: ', () => { } catch (error) { expect(error).toBeInstanceOf(InputValidationError); expect(error).toEqual( - new InputValidationError( - 'the length of the input arrays must be greater than 1' - ) + new InputValidationError(ARRAY_LENGTH_NON_EMPTY) ); } }); diff --git a/src/__tests__/unit/builtins/mock-observations.test.ts b/src/__tests__/unit/builtins/mock-observations.test.ts index 37d3a04b4..3e2ddab5b 100644 --- a/src/__tests__/unit/builtins/mock-observations.test.ts +++ b/src/__tests__/unit/builtins/mock-observations.test.ts @@ -1,10 +1,12 @@ import {MockObservations} from '../../../builtins/mock-observations'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError} = ERRORS; +const {InputValidationError, GlobalConfigError} = ERRORS; +const {INVALID_MIN_MAX} = STRINGS; -describe('lib/mock-observations: ', () => { +describe('builtins/mock-observations: ', () => { describe('init: ', () => { it('successfully initalized.', () => { const mockObservations = MockObservations({ @@ -88,8 +90,6 @@ describe('lib/mock-observations: ', () => { }); it('throws an error when the `min` is greater then `max` of `randint` config.', async () => { - const errorMessage = - 'RandIntGenerator: Min value should not be greater than or equal to max value of cpu/utilization.'; const config = { 'timestamp-from': '2023-07-06T00:00', 'timestamp-to': '2023-07-06T00:01', @@ -112,8 +112,10 @@ describe('lib/mock-observations: ', () => { try { await mockObservations.execute([]); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); - expect(error).toEqual(new InputValidationError(errorMessage)); + expect(error).toBeInstanceOf(GlobalConfigError); + expect(error).toEqual( + new GlobalConfigError(INVALID_MIN_MAX('cpu/utilization')) + ); } }); diff --git a/src/__tests__/unit/builtins/multiply.test.ts b/src/__tests__/unit/builtins/multiply.test.ts index a4451179d..7efceb325 100644 --- a/src/__tests__/unit/builtins/multiply.test.ts +++ b/src/__tests__/unit/builtins/multiply.test.ts @@ -1,10 +1,12 @@ import {Multiply} from '../../../builtins/multiply'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError} = ERRORS; +const {MissingInputDataError} = ERRORS; +const {MISSING_INPUT_DATA} = STRINGS; -describe('lib/multiply: ', () => { +describe('builtins/multiply: ', () => { describe('Multiply: ', () => { const globalConfig = { 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'], @@ -48,9 +50,6 @@ describe('lib/multiply: ', () => { }); it('throws an error on missing params in input.', async () => { - const expectedMessage = - 'Multiply: cpu/energy is missing from the input array.'; - expect.assertions(1); try { @@ -62,7 +61,7 @@ describe('lib/multiply: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new MissingInputDataError(MISSING_INPUT_DATA('cpu/energy')) ); } }); diff --git a/src/__tests__/unit/builtins/regex.test.ts b/src/__tests__/unit/builtins/regex.test.ts index 19ccdf89d..462faad8f 100644 --- a/src/__tests__/unit/builtins/regex.test.ts +++ b/src/__tests__/unit/builtins/regex.test.ts @@ -1,10 +1,12 @@ import {Regex} from '../../../builtins/regex'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError, ConfigValidationError} = ERRORS; +const {GlobalConfigError, MissingInputDataError, RegexMismatchError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_INPUT_DATA, REGEX_MISMATCH} = STRINGS; -describe('lib/regex: ', () => { +describe('builtins/regex: ', () => { describe('Regex: ', () => { const globalConfig = { parameter: 'physical-processor', @@ -81,7 +83,6 @@ describe('lib/regex: ', () => { it('throws an error when `parameter` does not match to `match`.', async () => { const physicalProcessor = 'Intel® Xeon® Platinum 8272CL,Intel® Xeon® 8171M 2.1 GHz,Intel® Xeon® E5-2673 v4 2.3 GHz,Intel® Xeon® E5-2673 v3 2.4 GHz'; - const expectedMessage = `Regex: \`${physicalProcessor}\` does not match the /^(^:)+/ regex expression.`; const globalConfig = { parameter: 'physical-processor', @@ -102,14 +103,14 @@ describe('lib/regex: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new RegexMismatchError( + REGEX_MISMATCH(physicalProcessor, '/^(^:)+/') + ) ); } }); it('throws an error on missing global config.', async () => { - const expectedMessage = 'Regex: Configuration data is missing.'; - const config = undefined; const regex = Regex(config!); @@ -124,15 +125,12 @@ describe('lib/regex: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new ConfigValidationError(expectedMessage) + new GlobalConfigError(MISSING_GLOBAL_CONFIG) ); } }); it('throws an error on missing params in input.', async () => { - const expectedMessage = - 'Regex: `physical-processor` is missing from the input.'; - expect.assertions(1); try { @@ -144,7 +142,7 @@ describe('lib/regex: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new MissingInputDataError(MISSING_INPUT_DATA('physical-processor')) ); } }); diff --git a/src/__tests__/unit/builtins/sci-embodied.test.ts b/src/__tests__/unit/builtins/sci-embodied.test.ts index 75f9f12fd..4b9f88deb 100644 --- a/src/__tests__/unit/builtins/sci-embodied.test.ts +++ b/src/__tests__/unit/builtins/sci-embodied.test.ts @@ -1,9 +1,12 @@ import {SciEmbodied} from '../../../builtins/sci-embodied'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; + const {InputValidationError} = ERRORS; +const {SCI_EMBODIED_ERROR} = STRINGS; -describe('lib/sci-embodied:', () => { +describe('builtins/sci-embodied:', () => { describe('SciEmbodied: ', () => { const sciEmbodied = SciEmbodied(); @@ -191,8 +194,6 @@ describe('lib/sci-embodied:', () => { }); it('throws an error when `device/emissions-embodied` is string.', async () => { - const errorMessage = - '"device/emissions-embodied" parameter is not a valid number in input. please provide it as `gco2e`. Error code: invalid_union.'; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', @@ -216,7 +217,13 @@ describe('lib/sci-embodied:', () => { try { await sciEmbodied.execute(inputs); } catch (error) { - expect(error).toStrictEqual(new InputValidationError(errorMessage)); + expect(error).toStrictEqual( + new InputValidationError( + `"device/emissions-embodied" parameter is ${SCI_EMBODIED_ERROR( + 'gco2e' + )}. Error code: invalid_union.` + ) + ); expect(error).toBeInstanceOf(InputValidationError); } }); @@ -268,8 +275,6 @@ describe('lib/sci-embodied:', () => { }); it('throws an exception on invalid values.', async () => { - const errorMessage = - '"device/emissions-embodied" parameter is not a valid number in input. please provide it as `gco2e`. Error code: invalid_union.'; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', @@ -285,8 +290,14 @@ describe('lib/sci-embodied:', () => { try { await sciEmbodied.execute(inputs); } catch (error) { - expect(error).toStrictEqual(new InputValidationError(errorMessage)); expect(error).toBeInstanceOf(InputValidationError); + expect(error).toStrictEqual( + new InputValidationError( + `"device/emissions-embodied" parameter is ${SCI_EMBODIED_ERROR( + 'gco2e' + )}. Error code: invalid_union.` + ) + ); } }); }); diff --git a/src/__tests__/unit/builtins/sci.test.ts b/src/__tests__/unit/builtins/sci.test.ts index 2b6261cda..a9401a5a8 100644 --- a/src/__tests__/unit/builtins/sci.test.ts +++ b/src/__tests__/unit/builtins/sci.test.ts @@ -2,9 +2,9 @@ import {Sci} from '../../../builtins/sci'; import {ERRORS} from '../../../util/errors'; -const {InputValidationError} = ERRORS; +const {MissingInputDataError} = ERRORS; -describe('lib/sci:', () => { +describe('builtins/sci:', () => { describe('Sci: ', () => { const sci = Sci({'functional-unit': 'users'}); @@ -113,7 +113,7 @@ describe('lib/sci:', () => { try { await sci.execute(inputs); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); + expect(error).toBeInstanceOf(MissingInputDataError); } }); @@ -136,7 +136,7 @@ describe('lib/sci:', () => { try { await sci.execute(inputs); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); + expect(error).toBeInstanceOf(MissingInputDataError); } }); }); diff --git a/src/__tests__/unit/builtins/shell.test.ts b/src/__tests__/unit/builtins/shell.test.ts index 7f1ffd58c..306dada3c 100644 --- a/src/__tests__/unit/builtins/shell.test.ts +++ b/src/__tests__/unit/builtins/shell.test.ts @@ -5,12 +5,12 @@ import {Shell} from '../../../builtins/shell'; import {ERRORS} from '../../../util/errors'; -const {InputValidationError} = ERRORS; +const {InputValidationError, ProcessExecutionError} = ERRORS; jest.mock('child_process'); jest.mock('js-yaml'); -describe('lib/shell', () => { +describe('builtins/shell', () => { describe('Shell', () => { const shell = Shell({}); @@ -87,9 +87,9 @@ describe('lib/shell', () => { try { await shell.execute(inputs); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); + expect(error).toBeInstanceOf(ProcessExecutionError); expect(error).toStrictEqual( - new InputValidationError('Could not run the command') + new ProcessExecutionError('Could not run the command') ); } }); diff --git a/src/__tests__/unit/builtins/subtract.test.ts b/src/__tests__/unit/builtins/subtract.test.ts index 7ac717142..2179adcd4 100644 --- a/src/__tests__/unit/builtins/subtract.test.ts +++ b/src/__tests__/unit/builtins/subtract.test.ts @@ -1,10 +1,12 @@ import {Subtract} from '../../../builtins/subtract'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; const {InputValidationError} = ERRORS; +const {MISSING_INPUT_DATA} = STRINGS; -describe('lib/subtract: ', () => { +describe('builtins/subtract: ', () => { describe('Subtract: ', () => { const globalConfig = { 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'], @@ -48,9 +50,6 @@ describe('lib/subtract: ', () => { }); it('throws an error on missing params in input.', async () => { - const expectedMessage = - 'Subtract: cpu/energy is missing from the input array.'; - expect.assertions(1); try { @@ -62,7 +61,7 @@ describe('lib/subtract: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new InputValidationError(MISSING_INPUT_DATA('cpu/energy')) ); } }); diff --git a/src/__tests__/unit/builtins/sum.test.ts b/src/__tests__/unit/builtins/sum.test.ts index 734ea8db2..e7b515f26 100644 --- a/src/__tests__/unit/builtins/sum.test.ts +++ b/src/__tests__/unit/builtins/sum.test.ts @@ -1,10 +1,12 @@ import {Sum} from '../../../builtins/sum'; import {ERRORS} from '../../../util/errors'; +import {STRINGS} from '../../../config'; -const {InputValidationError, ConfigNotFoundError} = ERRORS; +const {GlobalConfigError, MissingInputDataError} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_INPUT_DATA} = STRINGS; -describe('lib/sum: ', () => { +describe('builtins/sum: ', () => { describe('Sum: ', () => { const globalConfig = { 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'], @@ -48,7 +50,6 @@ describe('lib/sum: ', () => { }); it('throws an error when global config is not provided.', () => { - const expectedMessage = 'Global config is not provided.'; const config = undefined; const sum = Sum(config!); @@ -65,13 +66,13 @@ describe('lib/sum: ', () => { }, ]); } catch (error) { - expect(error).toStrictEqual(new ConfigNotFoundError(expectedMessage)); + expect(error).toStrictEqual( + new GlobalConfigError(MISSING_GLOBAL_CONFIG) + ); } }); it('throws an error on missing params in input.', () => { - const expectedMessage = 'cpu/energy is missing from the input array.'; - expect.assertions(1); try { @@ -83,7 +84,7 @@ describe('lib/sum: ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(expectedMessage) + new MissingInputDataError(MISSING_INPUT_DATA('cpu/energy')) ); } }); diff --git a/src/__tests__/unit/builtins/time-sync.test.ts b/src/__tests__/unit/builtins/time-sync.test.ts index 4839ad6ca..ac627fe02 100644 --- a/src/__tests__/unit/builtins/time-sync.test.ts +++ b/src/__tests__/unit/builtins/time-sync.test.ts @@ -1,11 +1,26 @@ +import {Settings, DateTime} from 'luxon'; + import {TimeSync} from '../../../builtins/time-sync'; + import {ERRORS} from '../../../util/errors'; -import {Settings, DateTime} from 'luxon'; + import {STRINGS} from '../../../config'; -Settings.defaultZone = 'utc'; -const {InputValidationError} = ERRORS; -const {INVALID_OBSERVATION_OVERLAP, INVALID_TIME_NORMALIZATION} = STRINGS; +Settings.defaultZone = 'utc'; +const { + InputValidationError, + InvalidPaddingError, + InvalidDateInInputError, + InvalidInputError, + GlobalConfigError, +} = ERRORS; + +const { + INVALID_OBSERVATION_OVERLAP, + INVALID_TIME_NORMALIZATION, + AVOIDING_PADDING_BY_EDGES, + INVALID_DATE_TYPE, +} = STRINGS; jest.mock('luxon', () => { const originalModule = jest.requireActual('luxon'); @@ -196,7 +211,7 @@ describe('execute(): ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(INVALID_TIME_NORMALIZATION) + new GlobalConfigError(INVALID_TIME_NORMALIZATION) ); } }); @@ -228,7 +243,7 @@ describe('execute(): ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(INVALID_OBSERVATION_OVERLAP) + new InvalidInputError(INVALID_OBSERVATION_OVERLAP) ); } }); @@ -258,7 +273,7 @@ describe('execute(): ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError(INVALID_OBSERVATION_OVERLAP) + new InvalidInputError(INVALID_OBSERVATION_OVERLAP) ); } }); @@ -335,22 +350,23 @@ describe('execute(): ', () => { interval: 10, 'allow-padding': true, }; + const data = [ + { + timestamp: 45, + duration: 10, + 'cpu/utilization': 10, + }, + ]; const timeModel = TimeSync(basicConfig); expect.assertions(2); try { - await timeModel.execute([ - { - timestamp: 45, - duration: 10, - 'cpu/utilization': 10, - }, - ]); + await timeModel.execute(data); } catch (error) { - expect(error).toBeInstanceOf(InputValidationError); + expect(error).toBeInstanceOf(InvalidDateInInputError); expect(error).toStrictEqual( - new InputValidationError('Unexpected date datatype: number: 45') + new InvalidDateInInputError(INVALID_DATE_TYPE(data[0].timestamp)) ); } }); @@ -675,7 +691,7 @@ describe('execute(): ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError('Avoiding padding at start') + new InvalidPaddingError(AVOIDING_PADDING_BY_EDGES(true, false)) ); } }); @@ -735,7 +751,7 @@ describe('execute(): ', () => { ]); } catch (error) { expect(error).toStrictEqual( - new InputValidationError('Avoiding padding at start and end') + new InvalidPaddingError(AVOIDING_PADDING_BY_EDGES(true, true)) ); } }); From 35b7657a032f9c0eb32df1492080488669a5e6e5 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 17:00:12 +0400 Subject: [PATCH 33/38] test(builtins): migrate errors, add cases to csv lookup --- .../unit/builtins/csv-lookup.test.ts | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/src/__tests__/unit/builtins/csv-lookup.test.ts b/src/__tests__/unit/builtins/csv-lookup.test.ts index 4e16fd9b6..8fbdee401 100644 --- a/src/__tests__/unit/builtins/csv-lookup.test.ts +++ b/src/__tests__/unit/builtins/csv-lookup.test.ts @@ -5,9 +5,19 @@ import AxiosMockAdapter from 'axios-mock-adapter'; import {CSVLookup} from '../../../builtins'; +import {STRINGS} from '../../../config'; + import {ERRORS} from '../../../util/errors'; -const {FileNotFoundError, InputValidationError, ConfigNotFoundError} = ERRORS; +const { + GlobalConfigError, + ReadFileError, + FetchingFileError, + QueryDataNotFoundError, + MissingCSVColumnError, + CSVParseError, +} = ERRORS; +const {MISSING_GLOBAL_CONFIG, MISSING_CSV_COLUMN, NO_QUERY_DATA} = STRINGS; describe('builtins/CSVLookup: ', () => { const mock = new AxiosMockAdapter(axios); @@ -131,7 +141,7 @@ describe('builtins/CSVLookup: ', () => { await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(FileNotFoundError); + expect(error).toBeInstanceOf(ReadFileError); } } }); @@ -160,7 +170,7 @@ describe('builtins/CSVLookup: ', () => { await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(FileNotFoundError); + expect(error).toBeInstanceOf(ReadFileError); } } }); @@ -192,7 +202,7 @@ describe('builtins/CSVLookup: ', () => { await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(FileNotFoundError); + expect(error).toBeInstanceOf(FetchingFileError); } } }); @@ -340,11 +350,8 @@ describe('builtins/CSVLookup: ', () => { await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(InputValidationError); - expect(error.message).toEqual( - `Error happened while parsing given CSV file: ./file.csv -InputValidationError: One or more of the given query parameters are not found in the target CSV file column headers.` - ); + expect(error).toBeInstanceOf(QueryDataNotFoundError); + expect(error.message).toEqual(NO_QUERY_DATA); } } }); @@ -368,8 +375,8 @@ InputValidationError: One or more of the given query parameters are not found in await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(ConfigNotFoundError); - expect(error.message).toEqual('Global config is not provided.'); + expect(error).toBeInstanceOf(GlobalConfigError); + expect(error.message).toEqual(MISSING_GLOBAL_CONFIG); } } }); @@ -400,10 +407,9 @@ InputValidationError: One or more of the given query parameters are not found in await csvLookup.execute(input); } catch (error) { if (error instanceof Error) { - expect(error).toBeInstanceOf(InputValidationError); + expect(error).toBeInstanceOf(MissingCSVColumnError); expect(error.message).toEqual( - `Error happened while parsing given CSV file: ./file.csv -InputValidationError: There is no column with name: mock.` + MISSING_CSV_COLUMN(globalConfig.output) ); } } @@ -477,5 +483,33 @@ InputValidationError: There is no column with name: mock.` expect(result).toStrictEqual(expectedResult); }); }); + + it('rejects with CSV parse error', async () => { + process.env.csv = 'fail'; + expect.assertions(1); + const globalConfig = { + filepath: './fail-csv-reader.csv', + query: { + 'cpu-cores-available': 'cpu/available', + 'cpu-cores-utilized': 'cpu/utilized', + 'cpu-manufacturer': 'cpu/manufacturer', + }, + output: [['gpu-count']], + }; + const csvLookup = CSVLookup(globalConfig); + + try { + await csvLookup.execute([ + { + timestamp: '2024-03-01', + 'cpu/available': 16, + 'cpu/utilized': 16, + 'cpu/manufacturer': 'AWS', + }, + ]); + } catch (error) { + expect(error).toBeInstanceOf(CSVParseError); + } + }); }); }); From efa095ef9765fafaeb2089fa36a021d2516046ed Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Mon, 10 Jun 2024 17:00:26 +0400 Subject: [PATCH 34/38] test(mocks): add invalid csv case to fs --- src/__mocks__/fs/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/__mocks__/fs/index.ts b/src/__mocks__/fs/index.ts index da8438c8b..ec83ff675 100644 --- a/src/__mocks__/fs/index.ts +++ b/src/__mocks__/fs/index.ts @@ -28,6 +28,13 @@ export const readFile = async (filePath: string) => { throw new Error('file not found'); } + if (filePath.includes('fail-csv-reader.csv')) { + return ` +cpu-cores-available,≈ç≈¬˚∆∑∂´®øˆ´cpu-cores-utilized, ---- cpu-manufacturer,cpu-model-name,cpu-tdp,gpu-count,gpu-model-name,Hardware Information on AWS Documentation & Comments,instance-class,instance-storage,memory-available,platform-memory,release-date,storage-drives +16,8,AWS,AWS Graviton +16,16,AWS,AWS Graviton,150.00,N/A,N/A,AWS Graviton (ARM),a1.4xlarge,EBS-Only,32,32,November 2018,`; + } + /** * Used for csv lookup plugin. */ From b78d49ad8618072049538607eb09a14040013cd9 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 12 Jun 2024 14:51:34 +0400 Subject: [PATCH 35/38] revert(builtins): remove unnecessary console.log --- src/builtins/csv-lookup/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/builtins/csv-lookup/index.ts b/src/builtins/csv-lookup/index.ts index 4f7df52be..5a009ac8b 100644 --- a/src/builtins/csv-lookup/index.ts +++ b/src/builtins/csv-lookup/index.ts @@ -193,7 +193,6 @@ export const CSVLookup = (globalConfig: any): ExecutePlugin => { const {filepath, query, output} = safeGlobalConfig; const file = await retrieveFile(filepath); - console.log(file); const parsedCSV = parseCSVFile(file); return inputs.map(input => { From a75e8d7c2cea2801ef2c4f7019bb30289d5a39bc Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 12 Jun 2024 14:52:12 +0400 Subject: [PATCH 36/38] test(util): remove unnecessary console.log --- src/__tests__/unit/util/aggregation-helper.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/unit/util/aggregation-helper.test.ts b/src/__tests__/unit/util/aggregation-helper.test.ts index 7cccbfccc..64add64e0 100644 --- a/src/__tests__/unit/util/aggregation-helper.test.ts +++ b/src/__tests__/unit/util/aggregation-helper.test.ts @@ -38,7 +38,6 @@ describe('util/aggregation-helper: ', () => { try { aggregateInputsIntoOne(inputs, metrics, isTemporal); } catch (error) { - console.log(error); expect(error).toBeInstanceOf(MissingAggregationParamError); if (error instanceof MissingAggregationParamError) { From d5d1ea9e15f2890b057a3e19948585b81d66f5a6 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Thu, 13 Jun 2024 19:52:02 +0400 Subject: [PATCH 37/38] revert(config): remove missing manifest from stdin message --- src/config/strings.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config/strings.ts b/src/config/strings.ts index 6ef27ac5e..83283e8fb 100644 --- a/src/config/strings.ts +++ b/src/config/strings.ts @@ -55,7 +55,6 @@ Note that for the '--output' option you also need to define the output type in y TARGET_IS_NOT_YAML: 'Given target is not in yaml format.', INVALID_TARGET: 'Target is invalid.', INVALID_SOURCE: 'Source is invalid.', - MISSING_MANIFEST_IN_STDIN: 'Manifest not found in STDIN.', /** Plugin messages */ MISSING_GLOBAL_CONFIG: 'Global config is not provided.', MISSING_INPUT_DATA: (param: string) => From f932b7dbdab916e0bebd49d2c517a1f5fbb8c166 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Thu, 13 Jun 2024 19:52:40 +0400 Subject: [PATCH 38/38] revert(util): remove unused error class and message --- src/util/helpers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 6b94c9397..d3b5a0a3c 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -9,8 +9,7 @@ import {STRINGS} from '../config'; import {Difference} from '../types/lib/compare'; -const {ISSUE_TEMPLATE, MISSING_MANIFEST_IN_STDIN} = STRINGS; -const {CliInputError} = ERRORS; +const {ISSUE_TEMPLATE} = STRINGS; /** * Impact engine error handler. Logs errors and appends issue template if error is unknown.