Skip to content

Commit

Permalink
test(jest): Add tests for configurations (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
SkiffCMC authored Oct 18, 2023
1 parent cd4a9eb commit 8dcd705
Show file tree
Hide file tree
Showing 2 changed files with 342 additions and 0 deletions.
186 changes: 186 additions & 0 deletions src/tests/configuration/dynamic-config-poller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/* eslint-disable global-require */
import {AppContext} from '../../lib/context';
import {Dict} from '../../types';

const MOCK_INTERVAL = 100;

jest.useFakeTimers({legacyFakeTimers: true});

jest.mock('axios', () => ({
__esModule: true,
default: {
get: jest
.fn()
.mockImplementation(
() =>
new Promise((resolve) =>
setTimeout(
() =>
resolve({
data: {
gravity: 9.81,
},
}),
1,
),
),
)
.mockImplementationOnce(
() =>
new Promise((_resolve, reject) =>
setTimeout(() => reject('URL did not respond'), 1),
),
)
.mockImplementationOnce(
() =>
new Promise((_resolve, reject) =>
setTimeout(() => reject('axios timeout'), 100),
),
)
.mockImplementationOnce(
() =>
new Promise((resolve) =>
setTimeout(
() =>
resolve({
data: {
gravity: 9.81,
},
}),
1,
),
),
)
.mockImplementationOnce(
() =>
new Promise((_resolve, reject) =>
setTimeout(() => reject('axios timeout'), 100),
),
),
},
}));

function logNothing(_message: string, _extra?: Dict) {}

const createMockAppContext = () => {
const mockCtx = new (AppContext as new (name: string, config: Object) => AppContext)('test', {
parentContext: false,
config: {},
logger: {},
utils: {},
tracer: {},
}) as jest.Mocked<AppContext>;
mockCtx.log = jest.fn().mockImplementation(logNothing);
mockCtx.logError = jest.fn().mockImplementation(logNothing);
return mockCtx;
};

const mockAppContext = createMockAppContext();

const spyOnLog = jest.spyOn(mockAppContext, 'log');
const spyOnLogError = jest.spyOn(mockAppContext, 'logError');

const proceedWithTicksAndTimers = async (iterations: number, interval: number = MOCK_INTERVAL) => {
for (let i = 0; i < iterations; i++) {
jest.advanceTimersByTime(interval);
await new Promise(process.nextTick);
}
};

const MOCK_DYNAMIC_CONFIG = {
url: 'mockUrl',
interval: MOCK_INTERVAL,
};

const baseEnv = process.env;

beforeEach(() => {
jest.clearAllMocks();
jest.resetModules();
process.env = {...baseEnv};
});

afterEach(() => {
process.env = baseEnv;
});

test('check if we successfully set ctx.dynamicConfig after several error calls', async () => {
//ARRANGE
const {DynamicConfigPoller} = require('../../lib/dynamic-config-poller');
const poller = new DynamicConfigPoller(mockAppContext, 'testPoller', MOCK_DYNAMIC_CONFIG);
//ACT
poller.startPolling();
const POLLING_CALLS = 3;
const SUCCESS_CALLS = 1;
const ERROR_CALLS = 2;
// we subtract 1 because we dont't care if our poller move to startPolling again after onSuccess in this test
await proceedWithTicksAndTimers(POLLING_CALLS + SUCCESS_CALLS + ERROR_CALLS - 1);
//ASSERT
expect(
(mockAppContext.dynamicConfig as Record<string, {gravity: number}>).testPoller.gravity,
).toEqual(9.81);
});

test('check if we do not rewrite already set ctx.dynamicConfig after error call', async () => {
//ARRANGE
const {DynamicConfigPoller} = require('../../lib/dynamic-config-poller');
const poller = new DynamicConfigPoller(mockAppContext, 'testPoller', MOCK_DYNAMIC_CONFIG);
//ACT
poller.startPolling();
const POLLING_CALLS = 4;
const SUCCESS_CALLS = 1;
const ERROR_CALLS = 3;
await proceedWithTicksAndTimers(POLLING_CALLS + SUCCESS_CALLS + ERROR_CALLS);
//ASSERT
expect(
(mockAppContext.dynamicConfig as Record<string, {gravity: number}>).testPoller.gravity,
).toEqual(9.81);
});

test('check if we continue to poll for config after success', async () => {
//ARRANGE
const {DynamicConfigPoller} = require('../../lib/dynamic-config-poller');
const poller = new DynamicConfigPoller(mockAppContext, 'testPoller', MOCK_DYNAMIC_CONFIG);
const spyOnStartPolling = jest.spyOn(poller, 'startPolling');
//ACT
poller.startPolling();
const POLLING_CALLS = 4;
const SUCCESS_CALLS = 1;
const ERROR_CALLS = 2;
await proceedWithTicksAndTimers(POLLING_CALLS + SUCCESS_CALLS + ERROR_CALLS);
//ASSERT
expect(spyOnStartPolling).toHaveBeenCalledTimes(POLLING_CALLS);
});

test('check if we log stuff with APP_DEBUG_DYNAMIC_CONFIG=debug', async () => {
//ARRANGE
process.env.APP_DEBUG_DYNAMIC_CONFIG = 'debug';
const {DynamicConfigPoller} = require('../../lib/dynamic-config-poller');
const poller = new DynamicConfigPoller(mockAppContext, 'testPoller', MOCK_DYNAMIC_CONFIG);
//ACT
poller.startPolling();
const POLLING_CALLS = 3;
const SUCCESS_CALLS = 1;
const ERROR_CALLS = 2;
// we subtract 1 because we dont't care if our poller move to startPolling again after onSuccess in this test
await proceedWithTicksAndTimers(POLLING_CALLS + SUCCESS_CALLS + ERROR_CALLS - 1);
//ASSERT
expect(spyOnLog).toHaveBeenCalledTimes(POLLING_CALLS + SUCCESS_CALLS);
expect(spyOnLogError).toHaveBeenCalledTimes(ERROR_CALLS);
});

test('check if we do not log stuff without APP_DEBUG_DYNAMIC_CONFIG flag', async () => {
//ARRANGE
const {DynamicConfigPoller} = require('../../lib/dynamic-config-poller');
const poller = new DynamicConfigPoller(mockAppContext, 'testPoller', MOCK_DYNAMIC_CONFIG);
//ACT
poller.startPolling();
const POLLING_CALLS = 3;
const SUCCESS_CALLS = 1;
const ERROR_CALLS = 2;
// we subtract 1 because we dont't care if our poller move to startPolling again after onSuccess in this test
await proceedWithTicksAndTimers(POLLING_CALLS + SUCCESS_CALLS + ERROR_CALLS - 1);
//ASSERT
expect(spyOnLog).not.toBeCalled();
expect(spyOnLogError).toHaveBeenCalledTimes(ERROR_CALLS);
});
156 changes: 156 additions & 0 deletions src/tests/configuration/file-configs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import path from 'path';

import {loadFileConfigs} from '../../lib/file-configs';

const TEST_MOCK_CONFIG = {
common: {
athmosphere: true,
gravity: 9.81,
},
mars: {
common: {
gravity: 3.71,
},
test: {
surface: 'marshmallow',
gravity: 1.23,
},
production: {
surface: 'sand',
},
},
venus: {
common: {
gravity: 8.87,
},
test: {
surface: 'nougat',
gravity: 4.56,
},
production: {
surface: 'stone',
},
},
} as Record<string, Record<string, unknown>>;

const MOCK_CONFIG_PATH = './mockConfigs';

jest.doMock(path.resolve(MOCK_CONFIG_PATH, `common`), () => TEST_MOCK_CONFIG.common, {
virtual: true,
});

['mars', 'venus'].forEach((installation) =>
['common', 'test', 'production'].forEach((environment) =>
jest.doMock(
path.resolve(MOCK_CONFIG_PATH, `${installation}/${environment}`),
() => TEST_MOCK_CONFIG[installation][environment],
{virtual: true},
),
),
);

test('check if we load right common config by default', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs');
//ASSERT
expect(gravity).toEqual(9.81);
});

test('check if we load specific common config without env', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'mars');
//ASSERT
expect(gravity).toEqual(3.71);
});

test('check if we load default common config if we messed with installation parameter', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'phaeton', 'test');
//ASSERT
expect(gravity).toEqual(9.81);
});

test('check if we load specific common config if we messed with env parameter', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'mars', 'model');
//ASSERT
expect(gravity).toEqual(3.71);
});

test('check if we do not overwrite existing parameters with non existing ones', () => {
//ARRANGE
//ACT
const {athmosphere} = loadFileConfigs('./mockConfigs', 'mars', 'test');
//ASSERT
expect(athmosphere).toEqual(true);
});

test('check if we correctly overwrite existing parameters', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'mars', 'production');
//ASSERT
expect(gravity).toEqual(3.71);
});

test('check if we correctly load existing parameters from specific configs', () => {
//ARRANGE
//ACT
const {surface} = loadFileConfigs('./mockConfigs', 'mars', 'test');
//ASSERT
expect(surface).toEqual('marshmallow');
});

test('check if we load different configs for different envs', () => {
//ARRANGE
//ACT
const {surface: testSurface} = loadFileConfigs('./mockConfigs', 'mars', 'test');
const {surface: productionSurface} = loadFileConfigs('./mockConfigs', 'mars', 'production');
//ASSERT
expect(testSurface === productionSurface).toEqual(false);
});

test('check if we load different configs for different installations', () => {
//ARRANGE
//ACT
const {gravity: marsSurface} = loadFileConfigs('./mockConfigs', 'mars', 'production');
const {gravity: venusSurface} = loadFileConfigs('./mockConfigs', 'venus', 'production');
//ASSERT
expect(marsSurface === venusSurface).toEqual(false);
});

test('check if we load specific common config if env and installation parameters are OK', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'mars', 'test');
//ASSERT
expect(gravity).toEqual(1.23);
});

test('check if we load default common config if we messed with env and installation parameters order', () => {
//ARRANGE
//ACT
const {gravity} = loadFileConfigs('./mockConfigs', 'test', 'mars');
//ASSERT
expect(gravity).toEqual(9.81);
});

test('check if we load empty config if we messed with configs path', () => {
//ARRANGE
//ACT
const config = loadFileConfigs('./noConfigs', 'mars', 'test');
//ASSERT
expect(Object.keys(config).length).toEqual(0);
});

test('check if we load empty config if we skip configs path', () => {
//ARRANGE
//ACT
const config = loadFileConfigs();
//ASSERT
expect(Object.keys(config).length).toEqual(0);
});

0 comments on commit 8dcd705

Please sign in to comment.