@autofleet/node-common 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ version: 2
2
+ jobs:
3
+ test:
4
+ docker:
5
+ - image: circleci/node:8.9.4
6
+ steps:
7
+ - checkout
8
+ - restore_cache:
9
+ keys:
10
+ - v1-dependencies-{{ checksum "package.json" }}
11
+ # fallback to using the latest cache if no exact match is found
12
+ - v1-dependencies-
13
+ - run: npm i
14
+ - save_cache:
15
+ paths:
16
+ - node_modules
17
+ key: v1-dependencies-{{ checksum "package.json" }}
18
+ - run: npm run coverage
19
+ - run: rm -rf ./coverage
20
+ - run: npm run linter
21
+
22
+ workflows:
23
+ version: 2
24
+ build_and_test:
25
+ jobs:
26
+ - test
package/.eslintrc.json CHANGED
@@ -4,8 +4,5 @@
4
4
  ],
5
5
  "env": {
6
6
  "jest": true
7
- },
8
- "rules": {
9
- "semi": ["error", "never"]
10
7
  }
11
8
  }
package/README.md CHANGED
@@ -12,7 +12,7 @@ Server 2 Servers communication.
12
12
 
13
13
  Implement:
14
14
  * Retriving service urls from environment
15
- * Retry - TBD
15
+ * Retry - Using https://github.com/softonic/axios-retry
16
16
  * Caching - TBD
17
17
  * Syntatic response for fail - TBD
18
18
  * Circuit Breaking - TBD
package/index.js CHANGED
@@ -1,4 +1,9 @@
1
+ const Network = require('./network');
2
+ const Logger = require('./logger');
3
+ const Settings = require('./settings');
4
+
1
5
  module.exports = {
2
- Network: require('./network'),
3
- Logger: require('./logger'),
4
- }
6
+ Network,
7
+ Logger,
8
+ Settings,
9
+ };
package/logger/example.js CHANGED
@@ -1,6 +1,5 @@
1
- const logger = require('./index')('debug')
1
+ const logger = require('./index')();
2
2
 
3
- logger.info('it is working')
4
- logger.error('errors displayed in logs/error.log')
5
- logger.debug('debug with object', { a: 5 })
6
- logger.debug('debug with 2 objects', { 1: { a: 5 }, 2: { b: 2 } })
3
+ logger.info('it is working');
4
+ logger.error('errors displayed in logs/error.log');
5
+ logger.debug('debug with object', { a: 5 });
package/logger/index.js CHANGED
@@ -1,64 +1,51 @@
1
- const { createLogger, transports, format } = require('winston')
1
+ const winston = require('winston');
2
2
 
3
- const { combine, timestamp, printf } = format
3
+ const { createLogger } = winston;
4
+ require('dotenv').config();
4
5
 
5
- require('dotenv').config()
6
+ const { env } = process;
6
7
 
7
- const { env } = process
8
+ const infoEnvNmaes = [
9
+ 'production',
10
+ 'staging',
11
+ 'test',
12
+ ];
8
13
 
9
14
  const getLevel = (logLevel) => {
10
- if (logLevel) return logLevel
11
- if (env.LOG_LEVEL) return env.LOG_LEVEL
12
- if (env.NODE_ENV === 'development') return 'debug'
13
- // test and producion will return info
14
- return 'info'
15
- }
16
-
17
- const getTransporters = () => {
18
- const consoleTrasporter = new transports.Console()
19
- let transporters = [consoleTrasporter]
20
- if (env.USE_LOG_FILES === 'true') {
21
- transporters = transporters.concat([
22
- new transports.File({ filename: 'logs/error.log', level: 'error' }),
23
- new transports.File({ filename: 'logs/all.log' }),
24
- ])
15
+ if (logLevel) return logLevel;
16
+ if (env.LOG_LEVEL) return env.LOG_LEVEL;
17
+ if (infoEnvNmaes.includes(env.NODE_ENV)) return 'info';
18
+ if (env.NODE_ENV === 'development') return 'debug';
19
+ return 'debug';
20
+ };
21
+
22
+ const getFormat = () => {
23
+ if (env.NODE_ENV === 'producation') {
24
+ return winston.format.json();
25
25
  }
26
- return transporters
27
- }
28
26
 
29
- const newLogger = (level, transporters) => {
30
- const customFormat = printf((info) => {
31
- const level = info.level
32
- const message = info.message
33
- const timestamp = info.timestamp
34
- delete info.level
35
- delete info.message
36
- delete info.timestamp
37
- const params = JSON.stringify(info, null, 2)
38
- return `${level}: ${message} ${timestamp} \n ${params} \n *****************`
39
- })
27
+ return winston.format.combine(
28
+ winston.format.splat(),
29
+ winston.format.colorize(),
30
+ winston.format.simple(),
31
+ );
32
+ };
40
33
 
34
+ const createLoggerInstance = (level, transports) => {
41
35
  const logger = createLogger({
42
- format: combine(
43
- format.colorize(),
44
- // format.json(),
45
- format.simple(),
46
- // prettyPrint(),
47
- timestamp(),
48
- customFormat,
49
- ),
50
36
  level,
51
- transports: transporters,
52
- })
37
+ format: getFormat(),
38
+ transports: [new winston.transports.Console()],
39
+ exceptionHandlers: transports,
40
+ exitOnError: false,
41
+ });
53
42
 
54
- return logger
55
- }
43
+ return logger;
44
+ };
56
45
 
57
46
  const getLogger = (logLevel) => {
58
- const level = getLevel(logLevel)
59
- const transporters = getTransporters()
60
- return newLogger(level, transporters)
61
- }
62
-
63
- module.exports = getLogger
47
+ const level = getLevel(logLevel);
48
+ return createLoggerInstance(level);
49
+ };
64
50
 
51
+ module.exports = getLogger;
@@ -1,108 +1,54 @@
1
- const Logger = require('./index')
2
- const fs = require('fs')
3
- const wait = require('wait-promise')
1
+ const Logger = require('./index');
4
2
 
5
- const { env } = process
6
-
7
- const deleteLogFile = (filePath) => {
8
- const dir = 'logs'
9
- if (!fs.existsSync(dir)) {
10
- fs.mkdirSync(dir)
11
- }
12
- if (fs.existsSync(filePath)) {
13
- fs.unlinkSync(filePath)
14
- }
15
- }
16
-
17
- const waitForFileToHaveContent = (filePath => wait.limit(30).until(() => {
18
- if (fs.existsSync(filePath)) {
19
- const content = fs.readFileSync(filePath, 'utf8')
20
- return content !== ''
21
- }
22
- return false
23
- }))
3
+ const { env } = process;
24
4
 
25
5
  describe('Logger', () => {
26
6
  it('it`s default level is info', () => {
27
- env.NODE_ENV = ''
28
- const logger = Logger()
29
- expect(logger).toBeDefined()
30
- expect(logger.level).toBe('info')
31
- })
7
+ env.NODE_ENV = '';
8
+ const logger = Logger();
9
+ expect(logger).toBeDefined();
10
+ expect(logger.level).toBe('debug');
11
+ });
32
12
 
33
13
  it('its default develpment level is debug', () => {
34
- env.NODE_ENV = 'development'
35
- const logger = Logger()
36
- expect(logger.level).toBe('debug')
37
- })
14
+ env.NODE_ENV = 'development';
15
+ const logger = Logger();
16
+ expect(logger.level).toBe('debug');
17
+ });
38
18
 
39
19
  it('its default test level is info', () => {
40
- env.NODE_ENV = 'test'
41
- const logger = Logger()
42
- expect(logger.level).toBe('info')
43
- })
20
+ env.NODE_ENV = 'test';
21
+ const logger = Logger();
22
+ expect(logger.level).toBe('info');
23
+ });
44
24
 
45
25
  it('its default production level is info', () => {
46
- env.NODE_ENV = 'production'
47
- const logger = Logger()
48
- expect(logger.level).toBe('info')
49
- })
26
+ env.NODE_ENV = 'production';
27
+ const logger = Logger();
28
+ expect(logger.level).toBe('info');
29
+ });
50
30
 
51
31
  it('its level can be override by LOG_LEVEL env variable', () => {
52
- env.NODE_ENV = 'development'
53
- env.LOG_LEVEL = 'warn'
54
- const logger = Logger()
55
- expect(logger.level).toBe('warn')
56
- })
32
+ env.NODE_ENV = 'development';
33
+ env.LOG_LEVEL = 'warn';
34
+ const logger = Logger();
35
+ expect(logger.level).toBe('warn');
36
+ });
57
37
 
58
38
  it('its level can be override by function variable', () => {
59
- env.NODE_ENV = 'production'
60
- env.LOG_LEVEL = 'warn'
61
- const logger = Logger('silly')
62
- expect(logger.level).toBe('silly')
63
- })
39
+ env.NODE_ENV = 'production';
40
+ env.LOG_LEVEL = 'warn';
41
+ const logger = Logger('silly');
42
+ expect(logger.level).toBe('silly');
43
+ });
64
44
 
65
45
  it('its has functions for each level', () => {
66
- const logger = Logger()
67
- expect(logger.error).toBeFunction()
68
- expect(logger.warn).toBeFunction()
69
- expect(logger.info).toBeFunction()
70
- expect(logger.verbose).toBeFunction()
71
- expect(logger.debug).toBeFunction()
72
- expect(logger.silly).toBeFunction()
73
- })
74
-
75
- it('it can have general file transporters', async () => {
76
- env.USE_LOG_FILES = 'true'
77
- const allPath = 'logs/all.log'
78
- deleteLogFile(allPath)
79
-
80
- const logger = Logger()
81
- const errorText = 'this is error'
82
- logger.error(errorText)
83
-
84
- await waitForFileToHaveContent(allPath)
85
-
86
- expect(fs.existsSync(allPath)).toBe(true)
87
-
88
- const contents = fs.readFileSync(allPath, 'utf8')
89
- expect(contents).toBe(`error: ${errorText}\n`)
90
- })
91
-
92
- it('it can have general file transporters', async () => {
93
- env.USE_LOG_FILES = 'true'
94
- const errorPath = 'logs/error.log'
95
- deleteLogFile(errorPath)
96
-
97
- expect(fs.existsSync(errorPath)).toBe(false)
98
-
99
- const logger = Logger()
100
- const errorText = 'this is error'
101
- logger.error(errorText)
102
-
103
- await waitForFileToHaveContent(errorPath)
104
-
105
- const contents = fs.readFileSync(errorPath, 'utf8')
106
- expect(contents).toBe(`error: ${errorText}\n`)
107
- })
108
- })
46
+ const logger = Logger();
47
+ expect(logger.error).toBeFunction();
48
+ expect(logger.warn).toBeFunction();
49
+ expect(logger.info).toBeFunction();
50
+ expect(logger.verbose).toBeFunction();
51
+ expect(logger.debug).toBeFunction();
52
+ expect(logger.silly).toBeFunction();
53
+ });
54
+ });
package/network/index.js CHANGED
@@ -1,12 +1,15 @@
1
- const axios = require('axios')
2
- const httpAdapter = require('axios/lib/adapters/http')
3
- require('dotenv').config()
1
+ const axios = require('axios');
2
+ const axiosRetry = require('axios-retry');
3
+ const Logger = require('../logger');
4
+ const qs = require('qs');
5
+ const httpAdapter = require('axios/lib/adapters/http');
6
+ require('dotenv').config();
4
7
  /**
5
8
  * Add support for nock testing
6
9
  * see s://github.com/axios/axios/issues/305
7
10
  */
8
11
  if (process.env.NODE_ENV === 'test') {
9
- axios.defaults.adapter = httpAdapter
12
+ axios.defaults.adapter = httpAdapter;
10
13
  }
11
14
 
12
15
  const HTTPMethods = [
@@ -17,36 +20,66 @@ const HTTPMethods = [
17
20
  'put',
18
21
  'patch',
19
22
  'options',
20
- ]
23
+ ];
21
24
 
22
25
  const defaultSettings = {
23
26
  timeout: 2500,
24
27
  headers: { 'X-AF-AUTH': 'ANYONE' },
25
- }
28
+ paramsSerializer: params => qs.stringify(params),
29
+ };
26
30
 
27
31
  module.exports = class Network {
28
32
  constructor(settings = {}) {
29
- this.settings = Object.assign(defaultSettings, settings)
30
- this.validate()
31
- this.createBaseUrl()
32
- this.buildClassHttpMethods()
33
+ this.settings = Object.assign({}, defaultSettings, settings);
34
+ this.createBaseUrl();
35
+
36
+ this.axios = axios.create(this.settings);
37
+ this.addRetry(settings);
38
+ this.buildClassHttpMethods();
39
+ this.addLogs();
33
40
  }
34
41
 
35
- validate() {
36
- if (!this.settings.serviceUrl && !this.settings.serviceName) {
37
- throw new Error('At least one of the settings Missing serviceUrl or serviceName')
38
- }
42
+ addRetry(settings) {
43
+ axiosRetry(this.axios, {
44
+ retries: 0,
45
+ retryDelay: axiosRetry.exponentialDelay,
46
+ ...settings,
47
+ });
48
+ }
49
+
50
+ addLogs() {
51
+ const logger = Logger();
52
+ this.axios.interceptors.request.use((request) => {
53
+ logger.info(`Start Request: [${request.method.toUpperCase()}] ${request.baseURL} ${request.url}`, {
54
+ headers: request.headers,
55
+ query: request.query,
56
+ body: request.body,
57
+ });
58
+ return request;
59
+ });
60
+
61
+ this.axios.interceptors.response.use((response) => {
62
+ logger.info(`Finish Request: [${response.config.method.toUpperCase()} ${response.config.url}`);
63
+ return response;
64
+ }, (response) => {
65
+ logger.error(`Faild Request: [${response.config.method.toUpperCase()}] ${response.config.url}`);
66
+ return response;
67
+ });
39
68
  }
40
69
 
41
70
  createBaseUrl() {
42
- const { settings } = this
71
+ if (!this.settings.serviceUrl && !this.settings.serviceName) {
72
+ throw new Error('At least one of the settings Missing serviceUrl or serviceName');
73
+ }
74
+
75
+ const { settings } = this;
43
76
  if (settings.serviceUrl) {
44
- settings.baseURL = settings.serviceUrl
77
+ settings.baseURL = settings.serviceUrl;
45
78
  } else if (settings.serviceName) {
46
- const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`
47
- settings.baseURL = `http://${process.env[envServiceHostName]}`
79
+ const envServiceHostName = `${settings.serviceName}_SERVICE_HOST`;
80
+ settings.baseURL = `http://${process.env[envServiceHostName]}`;
48
81
  if (!settings.baseURL) {
49
- throw new Error(`Missing environment variable: ${envServiceHostName}`)
82
+ throw new Error(`Missing environment variable: ${envServiceHostName}`);
50
83
  }
51
84
  }
52
85
  }
@@ -55,9 +88,8 @@ module.exports = class Network {
55
88
  * Build class methods that wrap axios methods
56
89
  */
57
90
  buildClassHttpMethods() {
58
- this.axios = axios.create(this.settings)
59
91
  HTTPMethods.forEach((method) => {
60
- this[method] = (...args) => this.axios[method](...args)
61
- })
92
+ this[method] = (...args) => this.axios[method](...args);
93
+ });
62
94
  }
63
- }
95
+ };
@@ -1,31 +1,68 @@
1
- const nock = require('nock')
2
- const Network = require('./index')
1
+ const nock = require('nock');
2
+ const Network = require('./index');
3
3
 
4
4
  nock('http://www.google.com')
5
5
  .get('/resource')
6
- .reply(200, { one: 1 })
6
+ .reply(200, { one: 1 });
7
7
 
8
8
 
9
9
  nock('https://www.apple.com')
10
10
  .get('/resource')
11
- .reply(200, { two: 2 })
11
+ .reply(200, { two: 2 });
12
12
 
13
13
  describe('Network', () => {
14
14
  it('Making requests by service name from ENV', async () => {
15
- process.env.TEST_SERVICE_HOST = 'www.google.com'
16
- const n = new Network({ serviceName: 'TEST' })
17
- const response = await n.get('/resource')
18
- expect(response).toBeDefined()
19
- expect(response.data).toBeDefined()
20
- expect(response.data.one).toBe(1)
21
- })
15
+ process.env.TEST_SERVICE_HOST = 'www.google.com';
16
+ const n = new Network({ serviceName: 'TEST' });
17
+ const response = await n.get('/resource');
18
+ expect(response).toBeDefined();
19
+ expect(response.data).toBeDefined();
20
+ expect(response.data.one).toBe(1);
21
+ });
22
22
 
23
- it('Making requests by service name from ENV', async () => {
24
- process.env.TEST_SERVICE_HOST = 'www.google.com'
25
- const n = new Network({ serviceUrl: 'https://www.apple.com' })
26
- const response = await n.get('/resource')
27
- expect(response).toBeDefined()
28
- expect(response.data).toBeDefined()
29
- expect(response.data.two).toBe(2)
30
- })
31
- })
23
+ it('Making requests by service name from url', async () => {
24
+ const n = new Network({ serviceUrl: 'https://www.apple.com' });
25
+ const response = await n.get('/resource');
26
+ expect(response).toBeDefined();
27
+ expect(response.data).toBeDefined();
28
+ expect(response.data.two).toBe(2);
29
+ });
30
+
31
+ it('Getting 404', async () => {
32
+ const n = new Network({ serviceUrl: 'https://www.apple.com' });
33
+ const response = await n.get('/resource', {
34
+ query: {
35
+ a: 'b',
36
+ },
37
+ });
38
+ expect(response).toBeDefined();
39
+ expect(response.data).not.toBeDefined();
40
+ });
41
+
42
+ test('Retry 1 time', async () => {
43
+ nock('https://www.apple2.com')
44
+ .get('/resource')
45
+ .once()
46
+ .reply(500);
47
+
48
+
49
+ nock('https://www.apple2.com')
50
+ .get('/resource')
51
+ .once()
52
+ .reply(200, { two: 2 });
53
+
54
+ const n = new Network({ serviceUrl: 'https://www.apple2.com' });
55
+ const response = await n.get('/resource', {
56
+ query: {
57
+ a: 'b',
58
+ },
59
+ 'axios-retry': {
60
+ retries: 1,
61
+ },
62
+ });
63
+
64
+ expect(response).toBeDefined();
65
+ expect(response.data).toBeDefined();
66
+ expect(response.data.two).toBe(2);
67
+ });
68
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/node-common",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "coverage": "jest --coverage --forceExit --runInBand",
@@ -23,10 +23,12 @@
23
23
  "homepage": "https://gitlab.com/AutoFleet/node-common#README",
24
24
  "dependencies": {
25
25
  "axios": "^0.18.0",
26
+ "axios-retry": "^3.1.0",
26
27
  "dotenv": "^5.0.1",
27
28
  "jest": "^22.4.3",
28
- "wait-promise": "^0.4.1",
29
- "winston": "^3.0.0-rc5"
29
+ "node-cache": "^4.2.0",
30
+ "qs": "^6.5.2",
31
+ "winston": "^3.0.0-rc4"
30
32
  },
31
33
  "devDependencies": {
32
34
  "eslint": "^4.19.1",
@@ -0,0 +1,30 @@
1
+ /* eslint no-console: 0 */
2
+ const Settings = require('./index');
3
+
4
+ const settings = new Settings({
5
+ serviceUrl: 'http://localhost:8085/',
6
+ });
7
+
8
+ const fleetId = 'e7bbfcad-98a8-421d-962f-fd1c7790c789';
9
+
10
+ const testSettings = async () => {
11
+ const generalSetting = await settings.get('ride.matching_retry', 1);
12
+ const fleetSetting = await settings.get('ride.matching_retry', 1, [{ fleetId }]);
13
+ const notFoundKey = await settings.get('not.found.key', 1, [{ fleetId }]);
14
+ await settings.get('not.found.key', 1, [{ fleetId }, { dor: 1 }]);
15
+
16
+ try {
17
+ await settings.get('not.found.key');
18
+ } catch (error) {
19
+ console.log(error);
20
+ }
21
+
22
+ console.log(generalSetting);
23
+ console.log(fleetSetting);
24
+ console.log(notFoundKey);
25
+ };
26
+
27
+ testSettings();
28
+ setTimeout(() => {
29
+ testSettings();
30
+ }, 500);
@@ -0,0 +1,52 @@
1
+ const NodeCache = require('node-cache');
2
+ const Network = require('../network');
3
+ const Logger = require('../logger');
4
+
5
+ const logger = Logger();
6
+ require('dotenv').config();
7
+
8
+ const fiveMinutes = 60 * 5;
9
+
10
+ module.exports = class SettingsManager {
11
+ constructor({ serviceUrl, ttl = fiveMinutes }) {
12
+ const localServiceUrl = serviceUrl || process.env.SETTING_MS_SERVICE_HOST;
13
+
14
+ this.ttl = ttl;
15
+ this.settingsCache = new NodeCache();
16
+ this.network = new Network({
17
+ serviceUrl: localServiceUrl,
18
+ });
19
+ }
20
+
21
+ async get(key, defaultValue, labels = []) {
22
+ if (!defaultValue) {
23
+ throw new Error('Can\'t get a key without defaultValue');
24
+ }
25
+
26
+ const cacheKey = `${key}_${JSON.stringify(labels)}`;
27
+ const cacheValue = this.settingsCache.get(cacheKey);
28
+ if (cacheValue !== undefined) {
29
+ return cacheValue;
30
+ }
31
+
32
+ let networkValue;
33
+ try {
34
+ const networkReponse = await this.network.get(`/api/v1/setting/get-setting/${key}`, {
35
+ params: {
36
+ labels,
37
+ },
38
+ });
39
+ networkValue = networkReponse.data.value;
40
+ } catch (error) {
41
+ logger.error('Cant get setting from network', error);
42
+ }
43
+
44
+ const returnValue = networkValue || defaultValue;
45
+ this.settingsCache.set(cacheKey, returnValue, this.ttl);
46
+ return returnValue;
47
+ }
48
+
49
+ flush() {
50
+ return this.settingsCache.flushAll();
51
+ }
52
+ };
@@ -0,0 +1,74 @@
1
+ const nock = require('nock');
2
+ const Settings = require('./index');
3
+
4
+ const serviceUrl = 'http://localhost:8085';
5
+
6
+ const mockSetting = (key, value, labels, response = 200) => nock(serviceUrl)
7
+ .get(`/api/v1/setting/get-setting/${key}`)
8
+ .query(labels ? { labels } : undefined)
9
+ .reply(response, { value });
10
+
11
+ describe('Settings', () => {
12
+ it('Get a key from server', async () => {
13
+ const m = mockSetting('key1', 'value1');
14
+ const settings = new Settings({
15
+ serviceUrl,
16
+ });
17
+
18
+ const value = await settings.get('key1', 'dv');
19
+ expect(value).toEqual('value1');
20
+ expect(m.isDone()).toBeTruthy();
21
+ });
22
+
23
+ it('Cache from server', async () => {
24
+ const m = mockSetting('key1', 'value1');
25
+ const settings = new Settings({
26
+ serviceUrl,
27
+ });
28
+
29
+ const value = await settings.get('key1', 'dv');
30
+ expect(value).toEqual('value1');
31
+ expect(m.isDone()).toBeTruthy();
32
+
33
+ const value2 = await settings.get('key1', 'dv');
34
+ expect(value2).toEqual('value1');
35
+ });
36
+
37
+ it('Finds with labels', async () => {
38
+ const m = mockSetting('key1', 'value1', [{ fleetId: 'uuid' }]);
39
+ const settings = new Settings({
40
+ serviceUrl,
41
+ });
42
+
43
+ const value = await settings.get('key1', 'dv', [{ fleetId: 'uuid' }]);
44
+ expect(value).toEqual('value1');
45
+ expect(m.isDone()).toBeTruthy();
46
+ });
47
+
48
+ it('Returns default value when network error', async () => {
49
+ const settings = new Settings({
50
+ serviceUrl,
51
+ });
52
+
53
+ const value = await settings.get('key1', 'dv', [{ fleetId: 'uuid' }]);
54
+ expect(value).toEqual('dv');
55
+ });
56
+
57
+ it('Values can be flushed', async () => {
58
+ const m1 = mockSetting('key1', 'value1');
59
+ const settings = new Settings({
60
+ serviceUrl,
61
+ });
62
+
63
+ const value = await settings.get('key1', 'dv');
64
+ expect(value).toEqual('value1');
65
+ expect(m1.isDone()).toBeTruthy();
66
+
67
+ settings.flush();
68
+ const m2 = mockSetting('key1', 'value2');
69
+
70
+ const value2 = await settings.get('key1', 'dv');
71
+ expect(value2).toEqual('value2');
72
+ expect(m2.isDone()).toBeTruthy();
73
+ });
74
+ });