import { describe, it } from 'node:test';
import { expect } from 'chai';
import { expectEvents } from '../expectEvents.ts';
import { expectPromise } from '../expectPromise.ts';
type TestTracingChannel = Parameters<typeof expectEvents>[0];
function createFakeTracingChannel(): TestTracingChannel {
let handler:
| {
start: (context: unknown) => void;
end: (context: unknown) => void;
asyncStart: (context: unknown) => void;
asyncEnd: (context: unknown) => void;
error: (context: unknown) => void;
}
| undefined;
function runStores<T>(
context: object,
fn: (this: object, ...args: Array<unknown>) => T,
thisArg?: unknown,
...args: Array<unknown>
): T {
return fn.apply((thisArg as object | undefined) ?? context, args);
}
return {
hasSubscribers: false,
subscribe(nextHandler) {
handler = nextHandler;
},
unsubscribe(nextHandler) {
expect(handler).to.equal(nextHandler);
handler = undefined;
},
traceSync(fn, _context, thisArg, ...args) {
return fn.apply(thisArg, args);
},
start: {
publish(context) {
handler?.start(context);
},
runStores,
},
end: {
publish(context) {
handler?.end(context);
},
runStores,
},
asyncStart: {
publish(context) {
handler?.asyncStart(context);
},
runStores,
},
asyncEnd: {
publish(context) {
handler?.asyncEnd(context);
},
runStores,
},
error: {
publish(context) {
handler?.error(context);
},
runStores,
},
};
}
describe('expectEvents', () => {
it('collects events and snapshots each published context', async () => {
const channel = createFakeTracingChannel();
const context = { value: 1 };
await expectEvents(
channel,
() => {
channel.start.publish(context);
context.value = 2;
channel.end.publish(context);
return 'done';
},
(_result) => [
{
channel: 'start',
context: { value: 1 },
},
{
channel: 'end',
context: { value: 2 },
},
],
);
});
it('collects events with non-object contexts', async () => {
const channel = createFakeTracingChannel();
await expectEvents(
channel,
() => {
channel.start.publish(null);
channel.end.publish(undefined);
channel.error.publish('error');
return 'done';
},
(_result) => [
{
channel: 'start',
context: null,
},
{
channel: 'end',
context: undefined,
},
{
channel: 'error',
context: 'error',
},
],
);
});
it('unsubscribes when the callback rejects', async () => {
let activeHandler: object | undefined;
const error = new Error('boom');
const channel = createFakeTracingChannel();
const originalSubscribe = channel.subscribe;
const originalUnsubscribe = channel.unsubscribe;
channel.subscribe = (handler) => {
activeHandler = handler;
originalSubscribe.call(channel, handler);
};
channel.unsubscribe = (handler) => {
expect(handler).to.equal(activeHandler);
activeHandler = undefined;
originalUnsubscribe.call(channel, handler);
};
expect(
await expectPromise(
expectEvents(
channel,
() => Promise.reject(error),
() => [],
),
).toReject(),
).to.equal(error);
expect(activeHandler).to.equal(undefined);
});
});