-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(jest): Add tests for configurations (#39)
- Loading branch information
Showing
2 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |