Self-echo filter for notify webhook (skip own-node messages) (#22)
CI / build + unit (push) Successful in 43s
CI / build + unit (push) Successful in 43s
This commit was merged in pull request #22.
This commit is contained in:
@@ -564,9 +564,23 @@ describe('MeshService message history', () => {
|
||||
|
||||
describe('MeshService notify webhook', () => {
|
||||
const fetchMock = vi.fn(async (..._args: unknown[]) => undefined);
|
||||
// our own node number for the self-echo filter specs (distinct from KAMIL_NUM, an external user)
|
||||
const MY_NODE = 987654321;
|
||||
|
||||
beforeEach(() => fetchMock.mockClear());
|
||||
|
||||
// myInfo frame as streamed during the initial sync — sets svc.myNodeNum
|
||||
function myInfoFrame(num: number): Uint8Array {
|
||||
return frame(
|
||||
toBinary(
|
||||
P.Mesh.FromRadioSchema,
|
||||
create(P.Mesh.FromRadioSchema, {
|
||||
payloadVariant: { case: 'myInfo', value: { myNodeNum: num } },
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
it('POSTs {text, from} to MESH_NOTIFY_URL when an rx text lands on the notify channel (default 3)', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
@@ -715,6 +729,66 @@ describe('MeshService notify webhook', () => {
|
||||
delete process.env.MESH_NOTIFY_CHANNEL;
|
||||
}
|
||||
});
|
||||
|
||||
it('does not call fetch for rx texts from our own node on the notify channel', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
process.env.MESH_PORT = String(bridge.port);
|
||||
process.env.MESH_NOTIFY_URL = 'http://notify.example/hook';
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const svc = new MeshService();
|
||||
try {
|
||||
svc.onModuleInit();
|
||||
await waitFor(
|
||||
() => bridge.sockets.length === 1 && svc.status().connected,
|
||||
);
|
||||
|
||||
// initial sync: our own node number arrives via myInfo first
|
||||
bridge.sockets[0].write(myInfoFrame(MY_NODE));
|
||||
await waitFor(() => svc.status().myNodeNum === MY_NODE);
|
||||
|
||||
// an rx text from ourselves on the notify channel must not fire the webhook
|
||||
bridge.sockets[0].write(textPacketFrame(MY_NODE, 42, 'ping', 3));
|
||||
|
||||
// history capture proves the packet was processed (notifyRx runs right after it)
|
||||
await waitFor(() => svc.messages().length === 1);
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
delete process.env.MESH_NOTIFY_URL;
|
||||
svc.onModuleDestroy();
|
||||
bridge.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not call fetch for an injected message from our own node', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
process.env.MESH_PORT = String(bridge.port);
|
||||
process.env.MESH_NOTIFY_URL = 'http://notify.example/hook';
|
||||
process.env.MESH_NOTIFY_CHANNEL = '0';
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const svc = new MeshService();
|
||||
try {
|
||||
svc.onModuleInit();
|
||||
await waitFor(
|
||||
() => bridge.sockets.length === 1 && svc.status().connected,
|
||||
);
|
||||
|
||||
bridge.sockets[0].write(myInfoFrame(MY_NODE));
|
||||
await waitFor(() => svc.status().myNodeNum === MY_NODE);
|
||||
|
||||
svc.injectMessage('hi', MY_NODE);
|
||||
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
delete process.env.MESH_NOTIFY_URL;
|
||||
delete process.env.MESH_NOTIFY_CHANNEL;
|
||||
svc.onModuleDestroy();
|
||||
bridge.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('MeshService injectMessage', () => {
|
||||
|
||||
@@ -434,6 +434,11 @@ export class MeshService implements OnModuleInit, OnModuleDestroy {
|
||||
/** Fire-and-forget notify-webhook POST for an rx text on `channel` (no-op unless URL is set and channels match). */
|
||||
private notifyRx(channel: number, text: string, from?: number): void {
|
||||
if (this.notifyUrl && channel === this.notifyChannel) {
|
||||
// self-echo filter: our own node must never trigger a notification of itself
|
||||
if (from !== undefined && from === this.myNodeNum) {
|
||||
this.logger.debug(`notify skipped: rx from our own node ${from}`);
|
||||
return;
|
||||
}
|
||||
const body: Record<string, unknown> = { text };
|
||||
if (from !== undefined) body.from = from;
|
||||
void fetch(this.notifyUrl, {
|
||||
|
||||
Reference in New Issue
Block a user