opentelemetry-js/packages/sdk-metrics/test/util.ts

216 lines
5.9 KiB
TypeScript

/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Context,
BatchObservableCallback,
MetricAttributes,
ObservableCallback,
ValueType,
} from '@opentelemetry/api';
import { InstrumentationScope } from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import * as assert from 'assert';
import {
InstrumentDescriptor,
InstrumentType,
} from '../src/InstrumentDescriptor';
import {
MetricData,
DataPoint,
DataPointType,
ScopeMetrics,
MetricDescriptor,
} from '../src/export/MetricData';
import { isNotNullish } from '../src/utils';
import { HrTime } from '@opentelemetry/api';
import { Histogram } from '../src/aggregator/types';
import { AggregationTemporality } from '../src/export/AggregationTemporality';
export type Measurement = {
value: number;
// TODO: use common attributes
attributes: MetricAttributes;
context?: Context;
};
export const defaultResource = Resource.default().merge(
new Resource({
resourceKey: 'my-resource',
})
);
export const defaultInstrumentDescriptor: InstrumentDescriptor = {
name: 'default_metric',
description: 'a simple instrument',
type: InstrumentType.COUNTER,
unit: '1',
valueType: ValueType.DOUBLE,
advice: {},
};
export const defaultInstrumentationScope: InstrumentationScope = {
name: 'default',
version: '1.0.0',
schemaUrl: 'https://opentelemetry.io/schemas/1.7.0',
};
export const invalidNames = ['', 'a'.repeat(256), '1a', '-a', '.a', '_a'];
export const validNames = [
'a',
'a'.repeat(255),
'a1',
'a-1',
'a.1',
'a_1',
'a/1',
];
export const commonValues: number[] = [1, -1, 1.0, Infinity, -Infinity, NaN];
export const commonAttributes: MetricAttributes[] = [
{},
{ 1: '1' },
{ a: '2' },
new (class Foo {
a = '1';
})(),
];
export const sleep = (time: number) =>
new Promise<void>(resolve => {
return setTimeout(resolve, time);
});
export function assertScopeMetrics(
actual: unknown,
instrumentationScope: Partial<InstrumentationScope>
): asserts actual is ScopeMetrics {
const it = actual as ScopeMetrics;
assertPartialDeepStrictEqual(it.scope, instrumentationScope);
assert(Array.isArray(it.metrics));
}
export function assertMetricData(
actual: unknown,
dataPointType?: DataPointType,
metricDescriptor: Partial<MetricDescriptor> | null = defaultInstrumentDescriptor,
aggregationTemporality?: AggregationTemporality
): asserts actual is MetricData {
const it = actual as MetricData;
if (metricDescriptor != null) {
assertPartialDeepStrictEqual(it.descriptor, metricDescriptor);
}
if (isNotNullish(dataPointType)) {
assert.strictEqual(it.dataPointType, dataPointType);
} else {
assert(isNotNullish(DataPointType[it.dataPointType]));
}
if (aggregationTemporality != null) {
assert.strictEqual(aggregationTemporality, it.aggregationTemporality);
}
assert(Array.isArray(it.dataPoints));
}
export function assertDataPoint(
actual: unknown,
attributes: MetricAttributes,
point: Histogram | number,
startTime?: HrTime,
endTime?: HrTime
): asserts actual is DataPoint<unknown> {
const it = actual as DataPoint<unknown>;
assert.deepStrictEqual(it.attributes, attributes);
assert.deepStrictEqual(it.value, point);
if (startTime) {
assert.deepStrictEqual(
it.startTime,
startTime,
'startTime should be equal'
);
} else {
assert(Array.isArray(it.startTime));
assert.strictEqual(it.startTime.length, 2, 'startTime should be equal');
}
if (endTime) {
assert.deepStrictEqual(it.endTime, endTime, 'endTime should be equal');
} else {
assert(Array.isArray(it.endTime));
assert.strictEqual(it.endTime.length, 2, 'endTime should be equal');
}
}
export function assertMeasurementEqual(
actual: unknown,
expected: Measurement
): asserts actual is Measurement {
// NOTE: Node.js v8 assert.strictEquals treat two NaN as different values.
if (Number.isNaN(expected.value)) {
assert(Number.isNaN((actual as Measurement).value));
} else {
assert.strictEqual((actual as Measurement).value, expected.value);
}
assert.deepStrictEqual(
(actual as Measurement).attributes,
expected.attributes
);
assert.deepStrictEqual((actual as Measurement).context, expected.context);
}
export function assertPartialDeepStrictEqual<T>(
actual: unknown,
expected: T,
message?: string
): asserts actual is T {
assert.strictEqual(typeof actual, typeof expected, message);
if (typeof expected !== 'object' && typeof expected !== 'function') {
return;
}
const ownNames = Object.getOwnPropertyNames(expected);
for (const ownName of ownNames) {
assert.deepStrictEqual(
(actual as any)[ownName],
(expected as any)[ownName],
`${ownName} not equals: ${message ?? '<no-message>'}`
);
}
}
export class ObservableCallbackDelegate {
private _delegate?: ObservableCallback;
setDelegate(delegate: ObservableCallback) {
this._delegate = delegate;
}
getCallback(): ObservableCallback {
return observableResult => {
return this._delegate?.(observableResult);
};
}
}
export class BatchObservableCallbackDelegate {
private _delegate?: BatchObservableCallback;
setDelegate(delegate: BatchObservableCallback) {
this._delegate = delegate;
}
getCallback(): BatchObservableCallback {
return observableResult => {
return this._delegate?.(observableResult);
};
}
}