Skip to content

Commit c6ff769

Browse files
committed
Add more tests
1 parent b8db5fa commit c6ff769

File tree

1 file changed

+356
-1
lines changed

1 file changed

+356
-1
lines changed

api/test/common/proxy-implementations/proxy-meter.test.ts

Lines changed: 356 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import {
2727
Counter,
2828
ObservableUpDownCounter,
2929
Gauge,
30+
Observable,
3031
} from '../../../src';
3132
import { NoopMeterProvider } from '../../../src/metrics/NoopMeterProvider';
3233
import { ProxyMeter } from '../../../src/metrics/ProxyMeter';
33-
import { NoopMeter } from '../../../src/metrics/NoopMeter';
34+
import { NoopMeter, NOOP_METER } from '../../../src/metrics/NoopMeter';
3435

3536
describe('ProxyMeter', () => {
3637
let provider: ProxyMeterProvider;
@@ -77,6 +78,36 @@ describe('ProxyMeter', () => {
7778
assert.doesNotThrow(() => histogram.record(1));
7879
assert.doesNotThrow(() => observable.addCallback(() => {}));
7980
});
81+
82+
it('creates additional synchronous instruments that remain no-ops before delegation', () => {
83+
const meter = provider.getMeter('test');
84+
85+
const gauge = meter.createGauge('gauge');
86+
const upDownCounter = meter.createUpDownCounter('upDown');
87+
88+
assert.doesNotThrow(() => gauge.record(5));
89+
assert.doesNotThrow(() => upDownCounter.add(-1));
90+
});
91+
92+
it('creates observable counters that buffer callbacks before delegation', () => {
93+
const meter = provider.getMeter('test');
94+
95+
const observableCounter = meter.createObservableCounter('observable-counter');
96+
const observableUpDownCounter = meter.createObservableUpDownCounter(
97+
'observable-up-down-counter'
98+
);
99+
const counterCallback = sandbox.stub();
100+
const upDownCallback = sandbox.stub();
101+
102+
assert.doesNotThrow(() => observableCounter.addCallback(counterCallback));
103+
assert.doesNotThrow(() => observableCounter.removeCallback(counterCallback));
104+
assert.doesNotThrow(() =>
105+
observableUpDownCounter.addCallback(upDownCallback)
106+
);
107+
assert.doesNotThrow(() =>
108+
observableUpDownCounter.removeCallback(upDownCallback)
109+
);
110+
});
80111
});
81112

82113
describe('when delegate is set before getMeter', () => {
@@ -212,6 +243,25 @@ describe('ProxyMeter', () => {
212243
const instrument = meter.createUpDownCounter('test');
213244
assert.strictEqual(instrument, delegateUpDownCounter);
214245
});
246+
247+
it('registers batch callbacks through the delegate once bound', () => {
248+
const proxyObservable = meter.createObservableGauge('proxy');
249+
const foreignObservable: ObservableGauge = {
250+
addCallback: sandbox.stub(),
251+
removeCallback: sandbox.stub(),
252+
};
253+
const callback = sandbox.stub();
254+
255+
meter.addBatchObservableCallback(callback, [
256+
proxyObservable,
257+
foreignObservable,
258+
]);
259+
260+
sandbox.assert.calledOnce(addBatchStub);
261+
const [, registeredObservables] = addBatchStub.firstCall.args;
262+
assert.strictEqual(registeredObservables[0], delegateObservableGauge);
263+
assert.strictEqual(registeredObservables[1], foreignObservable);
264+
});
215265
});
216266

217267
describe('when instruments are created before delegate is set', () => {
@@ -235,6 +285,122 @@ describe('ProxyMeter', () => {
235285
sandbox.assert.calledOnceWithExactly(addStub, 7);
236286
});
237287

288+
it('hydrates gauges that were created before delegation', () => {
289+
const meter = provider.getMeter('test');
290+
const gauge = meter.createGauge('pre-gauge');
291+
const recordStub = sandbox.stub();
292+
const delegateGauge: Gauge = {
293+
record: recordStub,
294+
} as Gauge;
295+
const delegateMeter = new NoopMeter();
296+
sandbox.stub(delegateMeter, 'createGauge').returns(delegateGauge);
297+
298+
provider.setDelegate({
299+
getMeter() {
300+
return delegateMeter;
301+
},
302+
});
303+
304+
gauge.record(9);
305+
sandbox.assert.calledOnceWithExactly(recordStub, 9);
306+
});
307+
308+
it('hydrates histograms that were created before delegation', () => {
309+
const meter = provider.getMeter('test');
310+
const histogram = meter.createHistogram('pre-histogram');
311+
const recordStub = sandbox.stub();
312+
const delegateHistogram: Histogram = {
313+
record: recordStub,
314+
} as Histogram;
315+
const delegateMeter = new NoopMeter();
316+
sandbox.stub(delegateMeter, 'createHistogram').returns(delegateHistogram);
317+
318+
provider.setDelegate({
319+
getMeter() {
320+
return delegateMeter;
321+
},
322+
});
323+
324+
histogram.record(33);
325+
sandbox.assert.calledOnceWithExactly(recordStub, 33);
326+
});
327+
328+
it('hydrates up down counters that were created before delegation', () => {
329+
const meter = provider.getMeter('test');
330+
const upDownCounter = meter.createUpDownCounter('pre-updown');
331+
const addStub = sandbox.stub();
332+
const delegateUpDownCounter: UpDownCounter = {
333+
add: addStub,
334+
} as UpDownCounter;
335+
const delegateMeter = new NoopMeter();
336+
sandbox.stub(delegateMeter, 'createUpDownCounter').returns(delegateUpDownCounter);
337+
338+
provider.setDelegate({
339+
getMeter() {
340+
return delegateMeter;
341+
},
342+
});
343+
344+
upDownCounter.add(-11);
345+
sandbox.assert.calledOnceWithExactly(addStub, -11);
346+
});
347+
348+
it('hydrates observable counters that were created before delegation', () => {
349+
const meter = provider.getMeter('test');
350+
const observableCounter = meter.createObservableCounter('pre-observable-counter');
351+
const callback = sandbox.stub();
352+
observableCounter.addCallback(callback);
353+
354+
const delegateObservableCounter: ObservableCounter = {
355+
addCallback: sandbox.stub(),
356+
removeCallback: sandbox.stub(),
357+
};
358+
const delegateMeter = new NoopMeter();
359+
sandbox
360+
.stub(delegateMeter, 'createObservableCounter')
361+
.returns(delegateObservableCounter);
362+
363+
provider.setDelegate({
364+
getMeter() {
365+
return delegateMeter;
366+
},
367+
});
368+
369+
sandbox.assert.calledOnceWithExactly(
370+
delegateObservableCounter.addCallback as sinon.SinonStub,
371+
callback
372+
);
373+
});
374+
375+
it('hydrates observable up down counters that were created before delegation', () => {
376+
const meter = provider.getMeter('test');
377+
const observableUpDownCounter = meter.createObservableUpDownCounter(
378+
'pre-observable-updown'
379+
);
380+
const callback = sandbox.stub();
381+
observableUpDownCounter.addCallback(callback);
382+
383+
const delegateObservableUpDownCounter: ObservableUpDownCounter = {
384+
addCallback: sandbox.stub(),
385+
removeCallback: sandbox.stub(),
386+
};
387+
const delegateMeter = new NoopMeter();
388+
sandbox
389+
.stub(delegateMeter, 'createObservableUpDownCounter')
390+
.returns(delegateObservableUpDownCounter);
391+
392+
provider.setDelegate({
393+
getMeter() {
394+
return delegateMeter;
395+
},
396+
});
397+
398+
sandbox.assert.calledOnceWithExactly(
399+
delegateObservableUpDownCounter.addCallback as sinon.SinonStub,
400+
callback
401+
);
402+
});
403+
238404
it('hydrates observable callbacks that were added before delegation', () => {
239405
const meter = provider.getMeter('test');
240406
const observable = meter.createObservableGauge('observable');
@@ -291,5 +457,194 @@ describe('ProxyMeter', () => {
291457
const [, registeredObservables] = addBatchStub.firstCall.args;
292458
assert.strictEqual(registeredObservables[0], delegateObservable);
293459
});
460+
461+
it('remaps proxy observables when registering batch callbacks after delegation', () => {
462+
const meter = provider.getMeter('test');
463+
const proxyObservable = meter.createObservableGauge('proxy-batch');
464+
const callback = sandbox.stub();
465+
const delegateObservable: ObservableGauge = {
466+
addCallback: sandbox.stub(),
467+
removeCallback: sandbox.stub(),
468+
};
469+
const delegateMeter = new NoopMeter();
470+
sandbox
471+
.stub(delegateMeter, 'createObservableGauge')
472+
.returns(delegateObservable);
473+
const addBatchStub = sandbox.stub(
474+
delegateMeter,
475+
'addBatchObservableCallback'
476+
);
477+
478+
provider.setDelegate({
479+
getMeter() {
480+
return delegateMeter;
481+
},
482+
});
483+
484+
meter.addBatchObservableCallback(callback, [proxyObservable]);
485+
486+
sandbox.assert.calledOnce(addBatchStub);
487+
const [, registeredObservables] = addBatchStub.firstCall.args;
488+
assert.strictEqual(registeredObservables[0], delegateObservable);
489+
});
490+
491+
it('removes batch callbacks via the current delegate before delegation', () => {
492+
const meter = provider.getMeter('test');
493+
const observable = meter.createObservableGauge('batch');
494+
const callback = sandbox.stub();
495+
496+
meter.addBatchObservableCallback(callback, [observable]);
497+
498+
const noopMeter = new NoopMeter();
499+
const getMeterStub = sandbox
500+
.stub(meter as unknown as { _getMeter: () => Meter }, '_getMeter')
501+
.returns(noopMeter);
502+
sandbox.stub(noopMeter, 'removeBatchObservableCallback');
503+
504+
assert.doesNotThrow(() =>
505+
meter.removeBatchObservableCallback(callback, [observable])
506+
);
507+
sandbox.assert.calledOnce(getMeterStub);
508+
sandbox.assert.calledOnceWithExactly(
509+
noopMeter.removeBatchObservableCallback as sinon.SinonStub,
510+
callback,
511+
[observable]
512+
);
513+
});
514+
515+
it('removes batch callbacks by delegating to the noop meter when unset', () => {
516+
const meter = provider.getMeter('test');
517+
const observable = meter.createObservableGauge('noop-batch');
518+
const callback = sandbox.stub();
519+
meter.addBatchObservableCallback(callback, [observable]);
520+
521+
const noopSpy = sandbox.spy(NOOP_METER, 'removeBatchObservableCallback');
522+
523+
meter.removeBatchObservableCallback(callback, [observable]);
524+
525+
sandbox.assert.calledOnce(noopSpy);
526+
});
527+
});
528+
529+
describe('proxy instrument internals', () => {
530+
it('does not track instruments that already have delegates', () => {
531+
const meter = provider.getMeter('test') as ProxyMeter;
532+
const privateMeter = meter as unknown as {
533+
_trackInstrument: (instrument: unknown) => void;
534+
_instruments: Set<unknown>;
535+
};
536+
privateMeter._instruments.clear();
537+
const instrument = {
538+
hasDelegate: sandbox.stub().returns(true),
539+
bindDelegate: sandbox.stub(),
540+
};
541+
542+
privateMeter._trackInstrument(instrument);
543+
544+
assert.strictEqual(privateMeter._instruments.size, 0);
545+
});
546+
547+
it('leaves non-proxy observables untouched when mapping delegates', () => {
548+
const meter = provider.getMeter('test') as ProxyMeter;
549+
const privateMeter = meter as unknown as {
550+
_mapObservablesToDelegates: (observables: Observable[]) => Observable[];
551+
};
552+
const observable: Observable = {
553+
addCallback: sandbox.stub(),
554+
removeCallback: sandbox.stub(),
555+
};
556+
557+
const [result] = privateMeter._mapObservablesToDelegates([observable]);
558+
559+
assert.strictEqual(result, observable);
560+
});
561+
562+
it('maps proxy observables to their delegates after binding', () => {
563+
const meter = provider.getMeter('test') as ProxyMeter;
564+
const privateMeter = meter as unknown as {
565+
_mapObservablesToDelegates: (observables: Observable[]) => Observable[];
566+
};
567+
const proxyObservable = meter.createObservableGauge('proxy');
568+
const delegateObservable: ObservableGauge = {
569+
addCallback: sandbox.stub(),
570+
removeCallback: sandbox.stub(),
571+
};
572+
const delegateMeter = new NoopMeter();
573+
sandbox
574+
.stub(delegateMeter, 'createObservableGauge')
575+
.returns(delegateObservable);
576+
577+
provider.setDelegate({
578+
getMeter() {
579+
return delegateMeter;
580+
},
581+
});
582+
583+
const [result] = privateMeter._mapObservablesToDelegates([
584+
proxyObservable as unknown as Observable,
585+
]);
586+
587+
assert.strictEqual(result, delegateObservable);
588+
});
589+
590+
it('does not bind pending instruments when no delegate is present', () => {
591+
const meter = provider.getMeter('test') as ProxyMeter;
592+
const privateMeter = meter as unknown as {
593+
_bindPendingInstruments: () => void;
594+
_instruments: Set<any>;
595+
};
596+
const instrument = {
597+
hasDelegate: sandbox.stub().returns(false),
598+
bindDelegate: sandbox.stub(),
599+
};
600+
privateMeter._instruments.add(instrument);
601+
602+
privateMeter._bindPendingInstruments();
603+
604+
sandbox.assert.notCalled(instrument.bindDelegate);
605+
assert.strictEqual(privateMeter._instruments.size, 1);
606+
});
607+
608+
it('falls back to NOOP meter when no delegate is available', () => {
609+
const meter = provider.getMeter('test') as ProxyMeter;
610+
const privateMeter = meter as unknown as { _getMeter: () => Meter };
611+
612+
const result = privateMeter._getMeter();
613+
614+
assert.strictEqual(result, NOOP_METER);
615+
});
616+
617+
it('allows proxy instruments to attempt delegate binding before delegation', () => {
618+
const meter = provider.getMeter('test');
619+
const counter = meter.createCounter('lazy');
620+
621+
assert.doesNotThrow(() =>
622+
(counter as unknown as { bindDelegate: () => void }).bindDelegate()
623+
);
624+
});
625+
626+
it('hydrates instruments lazily when pending state flush is prevented', () => {
627+
const meter = provider.getMeter('test') as ProxyMeter;
628+
const counter = meter.createCounter('lazy');
629+
const delegateCounter: Counter = {
630+
add: sandbox.stub(),
631+
} as Counter;
632+
const delegateMeter = new NoopMeter();
633+
sandbox.stub(delegateMeter, 'createCounter').returns(delegateCounter);
634+
sandbox.stub(meter as unknown as { _flushPendingState: () => void }, '_flushPendingState');
635+
636+
provider.setDelegate({
637+
getMeter() {
638+
return delegateMeter;
639+
},
640+
});
641+
642+
counter.add(3);
643+
644+
sandbox.assert.calledOnce(
645+
delegateMeter.createCounter as sinon.SinonStub
646+
);
647+
sandbox.assert.calledOnce(delegateCounter.add as sinon.SinonStub);
648+
});
294649
});
295650
});

0 commit comments

Comments
 (0)