Skip to content

Commit 3e0c960

Browse files
yaacovCRlogaretm
authored andcommitted
revamp diagnostics tests with full subscription data
1 parent 2a52b3f commit 3e0c960

14 files changed

Lines changed: 1700 additions & 477 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { catchThrownError } from '../catchThrownError.js';
5+
6+
describe('catchThrownError', () => {
7+
it('returns the thrown value', () => {
8+
const error = new Error('boom');
9+
10+
expect(
11+
catchThrownError(() => {
12+
throw error;
13+
}),
14+
).to.equal(error);
15+
});
16+
17+
it('throws when the function does not throw', () => {
18+
expect(() => catchThrownError(() => undefined)).to.throw(
19+
'Expected function to throw.',
20+
);
21+
});
22+
});
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { expectEvents } from '../expectEvents.js';
5+
import { expectPromise } from '../expectPromise.js';
6+
7+
type TestTracingChannel = Parameters<typeof expectEvents>[0];
8+
9+
function createFakeTracingChannel(): TestTracingChannel {
10+
let handler:
11+
| {
12+
start: (context: unknown) => void;
13+
end: (context: unknown) => void;
14+
asyncStart: (context: unknown) => void;
15+
asyncEnd: (context: unknown) => void;
16+
error: (context: unknown) => void;
17+
}
18+
| undefined;
19+
20+
function runStores<T>(
21+
context: object,
22+
fn: (this: object, ...args: Array<unknown>) => T,
23+
thisArg?: unknown,
24+
...args: Array<unknown>
25+
): T {
26+
return fn.apply((thisArg as object | undefined) ?? context, args);
27+
}
28+
29+
return {
30+
hasSubscribers: false,
31+
subscribe(nextHandler) {
32+
handler = nextHandler;
33+
},
34+
unsubscribe(nextHandler) {
35+
expect(handler).to.equal(nextHandler);
36+
handler = undefined;
37+
},
38+
traceSync(fn, _context, thisArg, ...args) {
39+
return fn.apply(thisArg, args);
40+
},
41+
start: {
42+
publish(context) {
43+
handler?.start(context);
44+
},
45+
runStores,
46+
},
47+
end: {
48+
publish(context) {
49+
handler?.end(context);
50+
},
51+
runStores,
52+
},
53+
asyncStart: {
54+
publish(context) {
55+
handler?.asyncStart(context);
56+
},
57+
runStores,
58+
},
59+
asyncEnd: {
60+
publish(context) {
61+
handler?.asyncEnd(context);
62+
},
63+
runStores,
64+
},
65+
error: {
66+
publish(context) {
67+
handler?.error(context);
68+
},
69+
runStores,
70+
},
71+
};
72+
}
73+
74+
describe('expectEvents', () => {
75+
it('collects events and snapshots each published context', async () => {
76+
const channel = createFakeTracingChannel();
77+
const context = { value: 1 };
78+
79+
await expectEvents(
80+
channel,
81+
() => {
82+
channel.start.publish(context);
83+
context.value = 2;
84+
channel.end.publish(context);
85+
return 'done';
86+
},
87+
(_result) => [
88+
{
89+
channel: 'start',
90+
context: { value: 1 },
91+
},
92+
{
93+
channel: 'end',
94+
context: { value: 2 },
95+
},
96+
],
97+
);
98+
});
99+
100+
it('unsubscribes when the callback rejects', async () => {
101+
let activeHandler: object | undefined;
102+
const error = new Error('boom');
103+
const channel = createFakeTracingChannel();
104+
const originalSubscribe = channel.subscribe;
105+
const originalUnsubscribe = channel.unsubscribe;
106+
107+
channel.subscribe = (handler) => {
108+
activeHandler = handler;
109+
originalSubscribe.call(channel, handler);
110+
};
111+
channel.unsubscribe = (handler) => {
112+
expect(handler).to.equal(activeHandler);
113+
activeHandler = undefined;
114+
originalUnsubscribe.call(channel, handler);
115+
};
116+
117+
expect(
118+
await expectPromise(
119+
expectEvents(
120+
channel,
121+
() => Promise.reject(error),
122+
() => [],
123+
),
124+
).toReject(),
125+
).to.equal(error);
126+
expect(activeHandler).to.equal(undefined);
127+
});
128+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { expectNoTracingActivity } from '../expectNoTracingActivity.js';
5+
import { expectPromise } from '../expectPromise.js';
6+
7+
type TestTracingChannel = Parameters<typeof expectNoTracingActivity>[0];
8+
9+
function createFakeTracingChannel(): TestTracingChannel {
10+
function runStores<T>(
11+
context: object,
12+
fn: (this: object, ...args: Array<unknown>) => T,
13+
thisArg?: unknown,
14+
...args: Array<unknown>
15+
): T {
16+
return fn.apply((thisArg as object | undefined) ?? context, args);
17+
}
18+
19+
return {
20+
hasSubscribers: false,
21+
traceSync(fn, context, thisArg, ...args) {
22+
return runStores(context, fn, thisArg, ...args);
23+
},
24+
start: {
25+
publish(_context) {
26+
return undefined;
27+
},
28+
runStores,
29+
},
30+
end: {
31+
publish(_context) {
32+
return undefined;
33+
},
34+
runStores,
35+
},
36+
asyncStart: {
37+
publish(_context) {
38+
return undefined;
39+
},
40+
runStores,
41+
},
42+
asyncEnd: {
43+
publish(_context) {
44+
return undefined;
45+
},
46+
runStores,
47+
},
48+
error: {
49+
publish(_context) {
50+
return undefined;
51+
},
52+
runStores,
53+
},
54+
};
55+
}
56+
57+
describe('expectNoTracingActivity', () => {
58+
it('returns the callback result when no tracing methods are touched', async () => {
59+
const channel = createFakeTracingChannel();
60+
61+
expect(
62+
await expectNoTracingActivity(channel, () => ({ value: 'ok' })),
63+
).to.deep.equal({ value: 'ok' });
64+
});
65+
66+
it('fails and restores methods when tracing activity occurs', async () => {
67+
const channel = createFakeTracingChannel();
68+
const originalPublish = channel.start.publish;
69+
70+
await expectPromise(
71+
expectNoTracingActivity(channel, () => {
72+
channel.start.publish({ value: 1 });
73+
}),
74+
).toRejectWith("expected [ 'start.publish' ] to deeply equal []");
75+
76+
expect(channel.start.publish).to.equal(originalPublish);
77+
});
78+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { interceptMethod } from '../interceptMethod.js';
5+
6+
describe('interceptMethod', () => {
7+
it('wraps a method and preserves this binding', () => {
8+
const calls: Array<number> = [];
9+
const target = {
10+
value: 3,
11+
add(delta: number): number {
12+
return this.value + delta;
13+
},
14+
};
15+
16+
const restore = interceptMethod(
17+
target,
18+
'add',
19+
(original) =>
20+
function interceptedAdd(this: unknown, ...args: Array<unknown>) {
21+
const [delta] = args as [number];
22+
calls.push(delta);
23+
return original.call(this, delta * 2);
24+
},
25+
);
26+
27+
expect(target.add(4)).to.equal(11);
28+
expect(calls).to.deep.equal([4]);
29+
30+
restore();
31+
32+
expect(target.add(4)).to.equal(7);
33+
});
34+
35+
it('restores the original method', () => {
36+
const target = {
37+
value(): string {
38+
return 'original';
39+
},
40+
};
41+
const original = target.value;
42+
43+
const restore = interceptMethod(target, 'value', () => () => 'wrapped');
44+
45+
expect(target.value()).to.equal('wrapped');
46+
47+
restore();
48+
49+
expect(target.value).to.equal(original);
50+
expect(target.value()).to.equal('original');
51+
});
52+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function catchThrownError(fn: () => unknown): unknown {
2+
try {
3+
fn();
4+
} catch (error) {
5+
return error;
6+
}
7+
8+
throw new Error('Expected function to throw.');
9+
}

0 commit comments

Comments
 (0)