Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests util #548

Merged
merged 8 commits into from
Mar 19, 2024
104 changes: 104 additions & 0 deletions src/__tests__/unit/config/strings.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
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 = `
[!important] 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';
Expand All @@ -19,6 +35,45 @@ describe('config/strings: ', () => {
});
});

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';
Expand All @@ -40,6 +95,37 @@ describe('config/strings: ', () => {
});
});

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';
Expand All @@ -50,6 +136,24 @@ describe('config/strings: ', () => {
});
});

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';
Expand Down
72 changes: 67 additions & 5 deletions src/__tests__/unit/util/aggregation-helper.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import {aggregateInputsIntoOne} from '../../../util/aggregation-helper';
import {ERRORS} from '../../../util/errors';

import {STRINGS} from '../../../config';

import {PluginParams} from '../../../types/interface';

const {InvalidAggregationParams} = ERRORS;
const {InvalidAggregationParamsError} = ERRORS;
const {INVALID_AGGREGATION_METHOD, METRIC_MISSING} = STRINGS;

describe('util/aggregation-helper: ', () => {
describe('aggregateInputsIntoOne(): ', () => {
Expand All @@ -12,12 +15,16 @@ describe('util/aggregation-helper: ', () => {
const metrics: string[] = ['cpu/number-cores'];
const isTemporal = false;

expect.assertions(1);
expect.assertions(2);

try {
aggregateInputsIntoOne(inputs, metrics, isTemporal);
} catch (error) {
expect(error).toBeInstanceOf(InvalidAggregationParams);
expect(error).toBeInstanceOf(InvalidAggregationParamsError);

if (error instanceof InvalidAggregationParamsError) {
expect(error.message).toEqual(INVALID_AGGREGATION_METHOD(metrics[0]));
}
}
});

Expand All @@ -26,13 +33,68 @@ describe('util/aggregation-helper: ', () => {
const metrics: string[] = ['cpu/utilization'];
const isTemporal = false;

expect.assertions(1);
expect.assertions(2);

try {
aggregateInputsIntoOne(inputs, metrics, isTemporal);
} catch (error) {
expect(error).toBeInstanceOf(InvalidAggregationParams);
expect(error).toBeInstanceOf(InvalidAggregationParamsError);

if (error instanceof InvalidAggregationParamsError) {
expect(error.message).toEqual(METRIC_MISSING(metrics[0], 0));
}
}
});

it('passes `timestamp`, `duration` to aggregator if aggregation is temporal.', () => {
const inputs: PluginParams[] = [
{timestamp: '', duration: 10, carbon: 10},
{timestamp: '', duration: 10, carbon: 20},
];
const metrics: string[] = ['carbon'];
const isTemporal = true;

const expectedValue = {
timestamp: '',
duration: 10,
carbon: inputs[0].carbon + inputs[1].carbon,
};
const aggregated = aggregateInputsIntoOne(inputs, metrics, isTemporal);
expect(aggregated).toEqual(expectedValue);
});

it('skips `timestamp`, `duration` if aggregation is not temporal.', () => {
const inputs: PluginParams[] = [
{timestamp: '', duration: 10, carbon: 10},
{timestamp: '', duration: 10, carbon: 20},
];
const metrics: string[] = ['carbon'];
const isTemporal = false;

const expectedValue = {
carbon: inputs[0].carbon + inputs[1].carbon,
};
const aggregated = aggregateInputsIntoOne(inputs, metrics, isTemporal);
expect(aggregated).toEqual(expectedValue);
});

it('calculates average of metrics.', () => {
const inputs: PluginParams[] = [
{timestamp: '', duration: 10, 'cpu/utilization': 10},
{timestamp: '', duration: 10, 'cpu/utilization': 90},
];
const metrics: string[] = ['cpu/utilization'];
const isTemporal = false;

const expectedValue = {
'cpu/utilization':
(inputs[0]['cpu/utilization'] + inputs[1]['cpu/utilization']) /
inputs.length,
};
const aggregated = aggregateInputsIntoOne(inputs, metrics, isTemporal);
expect(aggregated).toEqual(expectedValue);
expect(aggregated.timestamp).toBeUndefined();
expect(aggregated.duration).toBeUndefined();
});
});
});
40 changes: 40 additions & 0 deletions src/__tests__/unit/util/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const mockWarn = jest.fn();
const mockError = jest.fn();

jest.mock('../../../util/logger', () => ({
logger: {
warn: mockWarn,
error: mockError,
},
}));

import {andHandle} from '../../../util/helpers';
import {ERRORS} from '../../../util/errors';

const {WriteFileError} = ERRORS;

describe('util/helpers: ', () => {
describe('andHandle(): ', () => {
afterEach(() => {
mockWarn.mockReset();
mockError.mockReset();
});

it('logs error and warn in case of error is unknown.', () => {
const message = 'mock-message';
const MockError = class extends Error {};

andHandle(new MockError(message));
expect(mockWarn).toHaveBeenCalledTimes(1);
expect(mockError).toHaveBeenCalledTimes(1);
});

it('logs error in case of error is unknown.', () => {
const message = 'mock-message';

andHandle(new WriteFileError(message));
expect(mockWarn).toHaveBeenCalledTimes(0);
expect(mockError).toHaveBeenCalledTimes(1);
});
});
});
4 changes: 2 additions & 2 deletions src/builtins/group-by.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {STRINGS} from '../config';
import {GroupByPlugin, PluginParams} from '../types/interface';
import {GroupByConfig} from '../types/group-by';

const {InvalidGrouping} = ERRORS;
const {InvalidGroupingError} = ERRORS;

const {INVALID_GROUP_BY} = STRINGS;

Expand Down Expand Up @@ -56,7 +56,7 @@ export const GroupBy = (): GroupByPlugin => {
inputs.reduce((acc, input) => {
const groups = config.group.map(groupType => {
if (!input[groupType]) {
throw new InvalidGrouping(INVALID_GROUP_BY(groupType));
throw new InvalidGroupingError(INVALID_GROUP_BY(groupType));
}

return input[groupType];
Expand Down
3 changes: 1 addition & 2 deletions src/config/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Incubation projects are experimental, offer no support guarantee, have minimal g
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+-+
`,
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_TIME_NORMALIZATION: 'Start time or end time is missing.',
Expand Down
8 changes: 5 additions & 3 deletions src/util/aggregation-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {CONFIG, STRINGS} from '../config';
import {AggregationResult} from '../types/aggregation';
import {PluginParams} from '../types/interface';

const {InvalidAggregationParams} = ERRORS;
const {InvalidAggregationParamsError} = ERRORS;
const {INVALID_AGGREGATION_METHOD, METRIC_MISSING} = STRINGS;
const {AGGREGATION_ADDITIONAL_PARAMS} = CONFIG;

Expand All @@ -19,7 +19,9 @@ const checkIfMetricsAreValid = (metrics: string[]) => {
const method = parameterize.getAggregationMethod(metric);

if (method === 'none') {
throw new InvalidAggregationParams(INVALID_AGGREGATION_METHOD(metric));
throw new InvalidAggregationParamsError(
INVALID_AGGREGATION_METHOD(metric)
);
}
});
};
Expand All @@ -39,7 +41,7 @@ export const aggregateInputsIntoOne = (
return inputs.reduce((acc, input, index) => {
for (const metric of extendedMetrics) {
if (!(metric in input)) {
throw new InvalidAggregationParams(METRIC_MISSING(metric, index));
throw new InvalidAggregationParamsError(METRIC_MISSING(metric, index));
}

/** Checks if metric is timestamp or duration, then adds to aggregated value. */
Expand Down
4 changes: 2 additions & 2 deletions src/util/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const CUSTOM_ERRORS = [
'ManifestValidationError',
'ModuleInitializationError',
'InputValidationError',
'InvalidAggregationParams',
'InvalidGrouping',
'InvalidAggregationParamsError',
'InvalidGroupingError',
'PluginCredentialError',
'PluginInitalizationError',
'WriteFileError',
Expand Down
Loading