@emartech/aggregate-metric-logger 6.4.0 → 6.5.0
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.
- package/package.json +2 -2
- package/src/gcp-metric-collector/gcp-metric-collector.js +40 -2
- package/src/gcp-metric-collector/gcp-metric-collector.spec.js +200 -0
- package/src/gcp-time-series-builder/gcp-time-series-builder.js +29 -24
- package/src/index.d.ts +8 -0
- package/src/index.js +2 -0
- package/src/metric-types/gauge/gauge.js +20 -0
- package/src/metric-types/gauge/gauge.spec.js +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emartech/aggregate-metric-logger",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "aggregate metrics in memory for logging",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@emartech/rti-server-tools": "11.5.0",
|
|
41
41
|
"@types/jest": "30.0.0",
|
|
42
42
|
"dotenv-haphap": "3.1.2",
|
|
43
|
-
"eslint": "10.
|
|
43
|
+
"eslint": "10.9.0",
|
|
44
44
|
"globals": "17.11.0",
|
|
45
45
|
"jest": "30.4.2",
|
|
46
46
|
"semantic-release": "25.0.9"
|
|
@@ -5,8 +5,15 @@ const { createGcpTimeSeriesPublisher } = require('../gcp-time-series-publisher/g
|
|
|
5
5
|
const { createGcpTimeSeriesBuilder } = require('../gcp-time-series-builder/gcp-time-series-builder');
|
|
6
6
|
const { createCounter } = require('../metric-types/counter/counter');
|
|
7
7
|
const { createDistribution } = require('../metric-types/distribution/distribution');
|
|
8
|
+
const { createGauge } = require('../metric-types/gauge/gauge');
|
|
8
9
|
const { isNumber, isPlainObject } = require('lodash');
|
|
9
10
|
|
|
11
|
+
const aggregatorFactories = {
|
|
12
|
+
counter: createCounter,
|
|
13
|
+
distribution: createDistribution,
|
|
14
|
+
gauge: createGauge
|
|
15
|
+
};
|
|
16
|
+
|
|
10
17
|
class GcpMetricCollector {
|
|
11
18
|
metrics = {};
|
|
12
19
|
initialized = false;
|
|
@@ -33,6 +40,18 @@ class GcpMetricCollector {
|
|
|
33
40
|
return this.addToMetric(metricName, labels, 'distribution', value);
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
gauge(metricName, labels = {}, value) {
|
|
44
|
+
if (!isNumber(value)) {
|
|
45
|
+
throw new Error(`${ value } is not a number`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!isPlainObject(labels)) {
|
|
49
|
+
throw new Error('labels is not a plain object');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.addToMetric(metricName, labels, 'gauge', value);
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
start(metricName, labels = {}) {
|
|
37
56
|
return { metricName, labels, start: Date.now() };
|
|
38
57
|
}
|
|
@@ -55,7 +74,11 @@ class GcpMetricCollector {
|
|
|
55
74
|
}
|
|
56
75
|
const key = `${metricName}_${stringify(labels)}`;
|
|
57
76
|
if (!this.metrics[key]) {
|
|
58
|
-
const
|
|
77
|
+
const createAggregator = aggregatorFactories[type];
|
|
78
|
+
if (!createAggregator) {
|
|
79
|
+
throw new Error(`unknown metric type: ${type}`);
|
|
80
|
+
}
|
|
81
|
+
const aggregator = createAggregator();
|
|
59
82
|
this.metrics[key] = { name: metricName, labels, aggregator };
|
|
60
83
|
}
|
|
61
84
|
this.metrics[key].aggregator.add(value);
|
|
@@ -72,12 +95,22 @@ class GcpMetricCollector {
|
|
|
72
95
|
}
|
|
73
96
|
|
|
74
97
|
async flush() {
|
|
98
|
+
await this.publishPending();
|
|
99
|
+
this.setupNextFlush();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async shutdown() {
|
|
103
|
+
clearTimeout(this.timeoutId);
|
|
104
|
+
this.initialized = false;
|
|
105
|
+
await this.publishPending();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async publishPending() {
|
|
75
109
|
const timeSeries = createGcpTimeSeriesBuilder(this.projectId).buildTimeSeries(this.metrics);
|
|
76
110
|
for (const [, metric] of Object.entries(this.metrics)) {
|
|
77
111
|
metric.aggregator.advanceTimer();
|
|
78
112
|
}
|
|
79
113
|
await createGcpTimeSeriesPublisher(this.projectId).publishTimeSeries(timeSeries);
|
|
80
|
-
this.setupNextFlush();
|
|
81
114
|
}
|
|
82
115
|
}
|
|
83
116
|
|
|
@@ -89,10 +122,15 @@ exports.count = (metricName, labels = {}, value = 1) =>
|
|
|
89
122
|
exports.measure = (metricName, labels = {}, value = 1) =>
|
|
90
123
|
collector.measure(metricName, labels, value);
|
|
91
124
|
|
|
125
|
+
exports.gauge = (metricName, labels = {}, value) =>
|
|
126
|
+
collector.gauge(metricName, labels, value);
|
|
127
|
+
|
|
92
128
|
exports.start = (metricName, labels = {}) => collector.start(metricName, labels);
|
|
93
129
|
|
|
94
130
|
exports.stop = measurement => collector.stop(measurement);
|
|
95
131
|
|
|
96
132
|
exports.reset = () => collector.reset();
|
|
97
133
|
|
|
134
|
+
exports.shutdown = () => collector.shutdown();
|
|
135
|
+
|
|
98
136
|
exports.createGcpMetricsCollector = projectId => new GcpMetricCollector(projectId);
|
|
@@ -343,6 +343,160 @@ describe('gcpMetricCollector', () => {
|
|
|
343
343
|
});
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
+
describe('gauge', () => {
|
|
347
|
+
it('should send a single gauge value in next flush period', () => {
|
|
348
|
+
const now = new Date('2026-08-25T12:03:17+00:00');
|
|
349
|
+
const oneMinuteInMillis = 60 * 1000;
|
|
350
|
+
jest.useFakeTimers().setSystemTime(now);
|
|
351
|
+
stubCreateTimeSeries();
|
|
352
|
+
|
|
353
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
354
|
+
|
|
355
|
+
expect(getPublishedMetrics()).toEqual([]);
|
|
356
|
+
|
|
357
|
+
jest.advanceTimersByTime(oneMinuteInMillis);
|
|
358
|
+
|
|
359
|
+
expect(getPublishedMetrics()).toEqual([
|
|
360
|
+
[
|
|
361
|
+
{
|
|
362
|
+
resource: {
|
|
363
|
+
labels: {
|
|
364
|
+
project_id: 'dummy-project'
|
|
365
|
+
},
|
|
366
|
+
type: 'global'
|
|
367
|
+
},
|
|
368
|
+
name: 'job_delay',
|
|
369
|
+
labels: { programId: 'A' },
|
|
370
|
+
value: { doubleValue: 42 },
|
|
371
|
+
startTime: now.getTime() + oneMinuteInMillis,
|
|
372
|
+
endTime: now.getTime() + oneMinuteInMillis,
|
|
373
|
+
metricKind: 'GAUGE',
|
|
374
|
+
valueType: 'doubleValue'
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
]);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should keep only the last value within a flush period', async () => {
|
|
381
|
+
stubCreateTimeSeries();
|
|
382
|
+
|
|
383
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 5);
|
|
384
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 9);
|
|
385
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 3);
|
|
386
|
+
|
|
387
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
388
|
+
await flushPromises();
|
|
389
|
+
|
|
390
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
391
|
+
[{ name: 'job_delay', labels: { programId: 'A' }, value: { doubleValue: 3 } }]
|
|
392
|
+
]);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should send a separate metric for each label combo', async () => {
|
|
396
|
+
stubCreateTimeSeries();
|
|
397
|
+
|
|
398
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 1);
|
|
399
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'B' }, 2);
|
|
400
|
+
|
|
401
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
402
|
+
await flushPromises();
|
|
403
|
+
|
|
404
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
405
|
+
[
|
|
406
|
+
{ name: 'job_delay', labels: { programId: 'A' }, value: { doubleValue: 1 } },
|
|
407
|
+
{ name: 'job_delay', labels: { programId: 'B' }, value: { doubleValue: 2 } }
|
|
408
|
+
]
|
|
409
|
+
]);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should send metric for given gcp project id', () => {
|
|
413
|
+
stubCreateTimeSeries();
|
|
414
|
+
|
|
415
|
+
gcpMetricCollector
|
|
416
|
+
.createGcpMetricsCollector('test-project')
|
|
417
|
+
.gauge('job_delay', { programId: 'A' }, 42);
|
|
418
|
+
|
|
419
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
420
|
+
|
|
421
|
+
expect(getPublishedMetrics()).toEqual([
|
|
422
|
+
[
|
|
423
|
+
expect.objectContaining({
|
|
424
|
+
resource: {
|
|
425
|
+
labels: {
|
|
426
|
+
project_id: 'test-project'
|
|
427
|
+
},
|
|
428
|
+
type: 'global'
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
]
|
|
432
|
+
]);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should go silent and not republish a stale value when no new value is set', async () => {
|
|
436
|
+
stubCreateTimeSeries();
|
|
437
|
+
|
|
438
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
439
|
+
|
|
440
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
441
|
+
await flushPromises();
|
|
442
|
+
|
|
443
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
444
|
+
await flushPromises();
|
|
445
|
+
|
|
446
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
447
|
+
[{ name: 'job_delay', labels: { programId: 'A' }, value: { doubleValue: 42 } }]
|
|
448
|
+
]);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should publish a fresh value after a silent period', async () => {
|
|
452
|
+
stubCreateTimeSeries();
|
|
453
|
+
|
|
454
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
455
|
+
|
|
456
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
457
|
+
await flushPromises();
|
|
458
|
+
|
|
459
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
460
|
+
await flushPromises();
|
|
461
|
+
|
|
462
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 9);
|
|
463
|
+
|
|
464
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
465
|
+
await flushPromises();
|
|
466
|
+
|
|
467
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
468
|
+
[{ value: { doubleValue: 42 } }],
|
|
469
|
+
[{ value: { doubleValue: 9 } }]
|
|
470
|
+
]);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should not publish metrics if metric logger is not enabled', async () => {
|
|
474
|
+
process.env.METRIC_LOGGER_ENABLED = 'false';
|
|
475
|
+
stubCreateTimeSeries();
|
|
476
|
+
|
|
477
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
478
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
479
|
+
|
|
480
|
+
expect(getPublishedMetrics()).toEqual([]);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should validate labels parameter whether it is an object', async () => {
|
|
484
|
+
process.env.METRIC_LOGGER_ENABLED = 'false';
|
|
485
|
+
|
|
486
|
+
expect(() => {
|
|
487
|
+
gcpMetricCollector.gauge('irrelevant', new Error('not a plain object'), 42);
|
|
488
|
+
}).toThrow(new Error('labels is not a plain object'));
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should validate value parameter whether it is a number', async () => {
|
|
492
|
+
process.env.METRIC_LOGGER_ENABLED = 'false';
|
|
493
|
+
|
|
494
|
+
expect(() => {
|
|
495
|
+
gcpMetricCollector.gauge('irrelevant', {}, '42');
|
|
496
|
+
}).toThrow(new Error('42 is not a number'));
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
346
500
|
describe('start', () => {
|
|
347
501
|
it('should return a measurement object with the given tag, params and start time', () => {
|
|
348
502
|
const fakeCurrentTimestamp = 2323434543;
|
|
@@ -383,4 +537,50 @@ describe('gcpMetricCollector', () => {
|
|
|
383
537
|
]);
|
|
384
538
|
});
|
|
385
539
|
});
|
|
540
|
+
|
|
541
|
+
describe('shutdown', () => {
|
|
542
|
+
it('should flush pending metrics immediately', async () => {
|
|
543
|
+
stubCreateTimeSeries();
|
|
544
|
+
|
|
545
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
546
|
+
|
|
547
|
+
expect(getPublishedMetrics()).toEqual([]);
|
|
548
|
+
|
|
549
|
+
await gcpMetricCollector.shutdown();
|
|
550
|
+
|
|
551
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
552
|
+
[{ name: 'job_delay', labels: { programId: 'A' }, value: { doubleValue: 42 } }]
|
|
553
|
+
]);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should not schedule another flush after shutdown', async () => {
|
|
557
|
+
stubCreateTimeSeries();
|
|
558
|
+
const collector = gcpMetricCollector.createGcpMetricsCollector('test-project');
|
|
559
|
+
const publishPendingSpy = jest.spyOn(collector, 'publishPending');
|
|
560
|
+
|
|
561
|
+
collector.gauge('job_delay', { programId: 'A' }, 42);
|
|
562
|
+
await collector.shutdown();
|
|
563
|
+
|
|
564
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
565
|
+
await flushPromises();
|
|
566
|
+
|
|
567
|
+
expect(publishPendingSpy).toHaveBeenCalledTimes(1);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should resume flushing on the next metric after shutdown', async () => {
|
|
571
|
+
stubCreateTimeSeries();
|
|
572
|
+
|
|
573
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 42);
|
|
574
|
+
await gcpMetricCollector.shutdown();
|
|
575
|
+
|
|
576
|
+
gcpMetricCollector.gauge('job_delay', { programId: 'A' }, 9);
|
|
577
|
+
jest.advanceTimersByTime(60 * 1000 + 1);
|
|
578
|
+
await flushPromises();
|
|
579
|
+
|
|
580
|
+
expect(getPublishedMetrics()).toMatchObject([
|
|
581
|
+
[{ value: { doubleValue: 42 } }],
|
|
582
|
+
[{ value: { doubleValue: 9 } }]
|
|
583
|
+
]);
|
|
584
|
+
});
|
|
585
|
+
});
|
|
386
586
|
});
|
|
@@ -7,30 +7,35 @@ class GcpTimeSeriesBuilder {
|
|
|
7
7
|
this.projectId = projectId ?? process.env.MONITORING_PROJECT_ID;
|
|
8
8
|
}
|
|
9
9
|
buildTimeSeries(metrics) {
|
|
10
|
-
return Object.values(metrics)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
10
|
+
return Object.values(metrics)
|
|
11
|
+
.filter(
|
|
12
|
+
metric =>
|
|
13
|
+
typeof metric.aggregator.hasValue !== 'function' || metric.aggregator.hasValue()
|
|
14
|
+
)
|
|
15
|
+
.map(metric => {
|
|
16
|
+
const state = metric.aggregator.getState();
|
|
17
|
+
return {
|
|
18
|
+
metric: {
|
|
19
|
+
type: `custom.googleapis.com/${metric.name}`,
|
|
20
|
+
labels: { ...metric.labels, hostname: process.env.HOSTNAME }
|
|
21
|
+
},
|
|
22
|
+
resource: {
|
|
23
|
+
type: 'global',
|
|
24
|
+
labels: { project_id: this.projectId }
|
|
25
|
+
},
|
|
26
|
+
points: [
|
|
27
|
+
{
|
|
28
|
+
interval: {
|
|
29
|
+
startTime: { seconds: state.startTime / 1000 },
|
|
30
|
+
endTime: { seconds: state.endTime / 1000 }
|
|
31
|
+
},
|
|
32
|
+
value: state.value
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
metricKind: state.metricKind,
|
|
36
|
+
valueType: state.valueType
|
|
37
|
+
};
|
|
38
|
+
});
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
package/src/index.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ export function count(
|
|
|
4
4
|
value?: number
|
|
5
5
|
): void;
|
|
6
6
|
|
|
7
|
+
export function gauge(
|
|
8
|
+
metricName: string,
|
|
9
|
+
labels: MetricLabels,
|
|
10
|
+
value: number
|
|
11
|
+
): void;
|
|
12
|
+
|
|
7
13
|
export function measure(
|
|
8
14
|
metricName: string,
|
|
9
15
|
labels?: MetricLabels,
|
|
@@ -19,6 +25,8 @@ export function stop(
|
|
|
19
25
|
measurement: MetricTimerObject
|
|
20
26
|
): void;
|
|
21
27
|
|
|
28
|
+
export function shutdown(): Promise<void>;
|
|
29
|
+
|
|
22
30
|
export type MetricLabels = {
|
|
23
31
|
[key: string]: string | number | null | undefined
|
|
24
32
|
};
|
package/src/index.js
CHANGED
|
@@ -4,8 +4,10 @@ const gcpMetricCollector = require('./gcp-metric-collector/gcp-metric-collector'
|
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
count: gcpMetricCollector.count,
|
|
7
|
+
gauge: gcpMetricCollector.gauge,
|
|
7
8
|
measure: gcpMetricCollector.measure,
|
|
8
9
|
start: gcpMetricCollector.start,
|
|
9
10
|
stop: gcpMetricCollector.stop,
|
|
11
|
+
shutdown: gcpMetricCollector.shutdown,
|
|
10
12
|
createGcpMetricsCollector: gcpMetricCollector.createGcpMetricsCollector
|
|
11
13
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.createGauge = () => {
|
|
4
|
+
let value = null;
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
getState: () => ({
|
|
8
|
+
startTime: Date.now(),
|
|
9
|
+
endTime: Date.now(),
|
|
10
|
+
metricKind: 'GAUGE',
|
|
11
|
+
valueType: 'doubleValue',
|
|
12
|
+
value: { doubleValue: value }
|
|
13
|
+
}),
|
|
14
|
+
add: newValue => (value = newValue),
|
|
15
|
+
advanceTimer: () => {
|
|
16
|
+
value = null;
|
|
17
|
+
},
|
|
18
|
+
hasValue: () => value !== null
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createGauge } = require('./gauge');
|
|
4
|
+
|
|
5
|
+
describe('gauge', () => {
|
|
6
|
+
it('should return an empty gauge if no value was set', () => {
|
|
7
|
+
const now = new Date();
|
|
8
|
+
jest.useFakeTimers().setSystemTime(now);
|
|
9
|
+
const gauge = createGauge();
|
|
10
|
+
|
|
11
|
+
expect(gauge.getState()).toEqual({
|
|
12
|
+
startTime: now.getTime(),
|
|
13
|
+
endTime: now.getTime(),
|
|
14
|
+
metricKind: 'GAUGE',
|
|
15
|
+
valueType: 'doubleValue',
|
|
16
|
+
value: { doubleValue: null }
|
|
17
|
+
});
|
|
18
|
+
expect(gauge.hasValue()).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return the same startTime and endTime as it is an instantaneous point', () => {
|
|
22
|
+
jest.useFakeTimers().setSystemTime(new Date(8880));
|
|
23
|
+
const gauge = createGauge();
|
|
24
|
+
|
|
25
|
+
expect(gauge.getState().startTime).toBe(gauge.getState().endTime);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should store the value when it is set', () => {
|
|
29
|
+
const gauge = createGauge();
|
|
30
|
+
|
|
31
|
+
gauge.add(42);
|
|
32
|
+
|
|
33
|
+
expect(gauge.getState()).toMatchObject({ value: { doubleValue: 42 } });
|
|
34
|
+
expect(gauge.hasValue()).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should keep only the last value when multiple values are set', () => {
|
|
38
|
+
const gauge = createGauge();
|
|
39
|
+
|
|
40
|
+
gauge.add(5);
|
|
41
|
+
gauge.add(9);
|
|
42
|
+
gauge.add(3);
|
|
43
|
+
|
|
44
|
+
expect(gauge.getState()).toMatchObject({ value: { doubleValue: 3 } });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should support fractional values', () => {
|
|
48
|
+
const gauge = createGauge();
|
|
49
|
+
|
|
50
|
+
gauge.add(1.5);
|
|
51
|
+
|
|
52
|
+
expect(gauge.getState()).toMatchObject({ value: { doubleValue: 1.5 } });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should reset the value on advanceTimer call', () => {
|
|
56
|
+
const gauge = createGauge();
|
|
57
|
+
|
|
58
|
+
gauge.add(7);
|
|
59
|
+
gauge.advanceTimer();
|
|
60
|
+
|
|
61
|
+
expect(gauge.getState()).toMatchObject({ value: { doubleValue: null } });
|
|
62
|
+
expect(gauge.hasValue()).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should store a new value after advanceTimer call', () => {
|
|
66
|
+
const gauge = createGauge();
|
|
67
|
+
|
|
68
|
+
gauge.add(7);
|
|
69
|
+
gauge.advanceTimer();
|
|
70
|
+
gauge.add(2);
|
|
71
|
+
|
|
72
|
+
expect(gauge.getState()).toMatchObject({ value: { doubleValue: 2 } });
|
|
73
|
+
expect(gauge.hasValue()).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should return current time as endTime', () => {
|
|
77
|
+
jest.useFakeTimers().setSystemTime(new Date(880));
|
|
78
|
+
const gauge = createGauge();
|
|
79
|
+
|
|
80
|
+
jest.advanceTimersByTime(20);
|
|
81
|
+
|
|
82
|
+
expect(gauge.getState()).toMatchObject({ endTime: 900 });
|
|
83
|
+
});
|
|
84
|
+
});
|