Skip to content

Commit

Permalink
Merge pull request #60 from HubSpot/br-unit-tests-9
Browse files Browse the repository at this point in the history
Unit tests for fileSystem, cliConfig, and logger utils
  • Loading branch information
brandenrodgers authored Nov 14, 2023
2 parents 6e3799d + 8b3860c commit 72f7e68
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 4 deletions.
119 changes: 118 additions & 1 deletion config/__tests__/CLIConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,130 @@
import { ENVIRONMENTS } from '../../constants/environments';
import config from '../CLIConfiguration';

// TODO write tests for CLIConfiguration.ts
describe('config/CLIConfiguration', () => {
afterAll(() => {
config.setActive(false);
});

describe('constructor()', () => {
it('initializes correctly', () => {
expect(config).toBeDefined();
expect(config.options).toBeDefined();
expect(config.useEnvConfig).toBe(false);
expect(config.config).toBe(null);
expect(config.active).toBe(false);
});
});

describe('isActive()', () => {
it('returns true when the class is being used', () => {
expect(config.isActive()).toBe(false);
config.setActive(true);
expect(config.isActive()).toBe(true);
});
});

describe('getAccount()', () => {
it('returns null when no config is loaded', () => {
expect(config.getAccount('account-name')).toBe(null);
});
});

describe('isConfigFlagEnabled()', () => {
it('returns default value when no config is loaded', () => {
expect(config.isConfigFlagEnabled('allowUsageTracking', false)).toBe(
false
);
});
});

describe('getAccountId()', () => {
it('returns null when it cannot find the account in the config', () => {
expect(config.getAccountId('account-name')).toBe(null);
});
});

describe('getDefaultAccount()', () => {
it('returns null when no config is loaded', () => {
expect(config.getDefaultAccount()).toBe(null);
});
});

describe('getConfigAccountIndex()', () => {
it('returns -1 when no config is loaded', () => {
expect(config.getConfigAccountIndex(123)).toBe(-1);
});
});

describe('isAccountInConfig()', () => {
it('returns false when no config is loaded', () => {
expect(config.isAccountInConfig(123)).toBe(false);
});
});

describe('getConfigForAccount()', () => {
it('returns null when no config is loaded', () => {
expect(config.getConfigForAccount(123)).toBe(null);
});
});

describe('getEnv()', () => {
it('returns PROD when no config is loaded', () => {
expect(config.getEnv(123)).toBe(ENVIRONMENTS.PROD);
});
});

describe('updateDefaultAccount()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.updateDefaultAccount('account-name');
}).toThrow();
});
});

describe('renameAccount()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.renameAccount('account-name', 'new-account-name');
}).toThrow();
});
});

describe('removeAccountFromConfig()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.removeAccountFromConfig('account-name');
}).toThrow();
});
});

describe('updateDefaultMode()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.updateDefaultMode('newMode');
}).toThrow();
});
});

describe('updateHttpTimeout()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.updateHttpTimeout('1000');
}).toThrow();
});
});

describe('updateAllowUsageTracking()', () => {
it('throws when no config is loaded', () => {
expect(() => {
config.updateAllowUsageTracking(true);
}).toThrow();
});
});

describe('isTrackingAllowed()', () => {
it('returns true when no config is loaded', () => {
expect(config.isTrackingAllowed()).toBe(true);
});
});
});
28 changes: 28 additions & 0 deletions errors/__tests__/fileSystemErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { throwFileSystemError } from '../fileSystemErrors';
import { BaseError } from '../../types/Error';

export const newError = (overrides = {}): BaseError => {
return {
name: 'Error',
message: 'An error ocurred',
errno: 1,
code: 'error_code',
syscall: 'error_syscall',
errors: [],
...overrides,
};
};

const fileSystemErrorContext = {
filepath: 'some/path',
};

describe('errors/fileSystemErrors', () => {
describe('throwFileSystemError()', () => {
it('throws a fileSystemError', () => {
expect(() => {
throwFileSystemError(newError(), fileSystemErrorContext);
}).toThrow();
});
});
});
3 changes: 3 additions & 0 deletions errors/fileSystemErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { BaseError, FileSystemErrorContext } from '../types/Error';

const i18nKey = 'errors.fileSystemErrors';

/**
* @throws
*/
export function throwFileSystemError(
error: BaseError,
context: FileSystemErrorContext
Expand Down
146 changes: 146 additions & 0 deletions lib/__tests__/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import chalk from 'chalk';
import {
Styles,
stylize,
setLogLevel,
getLogLevel,
LOG_LEVEL,
logger,
shouldLog,
} from '../logging/logger';

describe('lib/logging/logger', () => {
afterEach(() => {
setLogLevel(LOG_LEVEL.LOG);
});

describe('stylize()', () => {
it('stylizes input', () => {
const res = stylize('[ERROR]', Styles.error, ['test']);

expect(res[0]).toEqual(`${chalk.reset.red('[ERROR]')} test`);
});
});

describe('setLogLevel()', () => {
it('sets the log level for the logger', () => {
setLogLevel(LOG_LEVEL.DEBUG);
expect(getLogLevel()).toBe(LOG_LEVEL.DEBUG);

setLogLevel(LOG_LEVEL.WARN);
expect(getLogLevel()).toBe(LOG_LEVEL.WARN);

setLogLevel(LOG_LEVEL.NONE);
expect(getLogLevel()).toBe(LOG_LEVEL.NONE);
});
});

describe('shouldLog()', () => {
it('returns false for all logs when the currentLogLevel is NONE', () => {
setLogLevel(LOG_LEVEL.NONE);
expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.WARN)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.ERROR)).toBeFalsy();
});

it('returns true for all logs when the currentLogLevel is DEBUG', () => {
setLogLevel(LOG_LEVEL.DEBUG);
expect(shouldLog(LOG_LEVEL.DEBUG)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.LOG)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy();
});

it('returns false for debugs when the currentLogLevel is LOG', () => {
setLogLevel(LOG_LEVEL.LOG);
expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.LOG)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy();
});

it('returns false for debugs and logs when the currentLogLevel is WARN', () => {
setLogLevel(LOG_LEVEL.WARN);
expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.WARN)).toBeTruthy();
expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy();
});

it('returns false for debugs, logs, and warns when the currentLogLevel is ERROR', () => {
setLogLevel(LOG_LEVEL.ERROR);
expect(shouldLog(LOG_LEVEL.DEBUG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.LOG)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.WARN)).toBeFalsy();
expect(shouldLog(LOG_LEVEL.ERROR)).toBeTruthy();
});
});

describe('logger()', () => {
let warnSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
let infoSpy: jest.SpyInstance;
let groupSpy: jest.SpyInstance;
let groupEndSpy: jest.SpyInstance;

beforeEach(() => {
setLogLevel(LOG_LEVEL.LOG);
warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => null);
logSpy = jest.spyOn(console, 'log').mockImplementation(() => null);
debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => null);
infoSpy = jest.spyOn(console, 'info').mockImplementation(() => null);
groupSpy = jest.spyOn(console, 'group').mockImplementation(() => null);
groupEndSpy = jest
.spyOn(console, 'groupEnd')
.mockImplementation(() => null);
});

afterAll(() => {
warnSpy.mockReset();
logSpy.mockReset();
debugSpy.mockReset();
infoSpy.mockReset();
groupSpy.mockReset();
groupEndSpy.mockReset();
});

it('handles warnings', () => {
logger.log('test log');
expect(warnSpy).not.toHaveBeenCalled();

logger.warn('test log');
expect(warnSpy).toHaveBeenCalled();
});

it('handles logs', () => {
logger.debug('test log');
expect(debugSpy).not.toHaveBeenCalled();

logger.log('test log');
expect(logSpy).toHaveBeenCalled();

setLogLevel(LOG_LEVEL.DEBUG);
logger.debug('test log');
expect(debugSpy).toHaveBeenCalled();
});

it('handles info', () => {
logger.info('test log');
expect(infoSpy).toHaveBeenCalled();
});

it('handles success', () => {
logger.success('test log');
expect(logSpy).toHaveBeenCalled();
});

it('handles group and groupEnd', () => {
logger.group('test log');
logger.groupEnd();
expect(groupSpy).toHaveBeenCalled();
expect(groupEndSpy).toHaveBeenCalled();
});
});
});
23 changes: 23 additions & 0 deletions lib/__tests__/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { commaSeparatedValues } from '../text';

describe('lib/text', () => {
describe('commaSeparatedValues()', () => {
it('returns a string with comma separated values', () => {
const res = commaSeparatedValues(['first', 'second', 'third']);

expect(res).toBe('first, second, and third');
});

it('supports a custom conjuction', async () => {
const res = commaSeparatedValues(['first', 'second', 'third'], 'custom');

expect(res).toBe('first, second, custom third');
});

it('supports a custom if empty case', async () => {
const res = commaSeparatedValues([], null, 'input is empty');

expect(res).toBe('input is empty');
});
});
});
4 changes: 2 additions & 2 deletions lib/logging/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ export function setLogLevel(level: number): void {
}
}

export function shouldLog(level: number): number {
return currentLogLevel & level;
export function shouldLog(level: number): boolean {
return !!(currentLogLevel & level);
}

export function getLogLevel(): number {
Expand Down
2 changes: 1 addition & 1 deletion lib/text.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export function commaSeparatedValues(
arr: Array<string>,
conjunction = 'and',
conjunction: null | string = 'and',
ifempty = ''
): string {
const l = arr.length;
Expand Down

0 comments on commit 72f7e68

Please sign in to comment.