Skip to content

Leaking node signal listener after server.stop() #503

@aMOPel

Description

@aMOPel

Describe the bug
Currently you're adding a signal listener for SIGINT to the servers process when calling server.start():

process.on("SIGINT", () => {

However this listener is never removed when calling server.stop() over the js api, effectively leaking it.

To Reproduce
I only noticed this because I was writing deno tests with this your great library and deno tests do some sanitazing checks:

https://docs.deno.com/runtime/fundamentals/testing/#sanitizers

Here is a minimal reproduction:

import { assert } from "@std/assert";
import { createServer } from "npm:@mocks-server/main";

async function initMockServer(routes: any, collections: any) {
    const server = createServer();
    await server.start()
    const { loadRoutes, loadCollections } = server.mock.createLoaders();
    loadRoutes(routes);
    loadCollections(collections);
    return server;
}

const fixtures = {
    routes: [
        {
            id: "get-users",
            url: "/api/users",
            method: "GET",
            variants: [
                {
                    id: "success",
                    type: "json",
                    options: {
                        status: 200,
                        body: { hi: "hi" },
                    },
                },
            ],
        },
    ],

    collections: [
        {
            id: "base",
            routes: ["get-users:success"],
        },
    ],
};

Deno.test("test", async (t) => {
    const server = await initMockServer(fixtures.routes, fixtures.collections);

    await server.mock.collections.select("base");
    assert(true, "works")

    await server.stop();
});
  1. mkdir -p ~/bug-repro && cd ~/bug-repro
  2. deno init
  3. paste code above into main_test.ts
  4. deno test --allow-all --trace-leaks

It will print something along these lines:

running 1 test from ./main_test.ts
test ...
------- output -------
14:41:49:76 [info][config] Configuration loaded
14:41:49:80 [info][server] Server started and listening at http://localhost:3100
14:41:49:80 [info][plugins:adminApi:server] Server started and listening at http://localhost:3110
14:41:49:81 [warn][alerts:collections] Option 'mock.collections.selected' was not defined. Selecting the first collection found
14:41:49:82 [info][mock] Selected collection: 'base'
14:41:49:82 [info][mock] Selected collection: 'base'
14:41:49:82 [info][server] Server stopped
14:41:49:82 [info][plugins:adminApi:server] Server stopped
----- output end -----
test ... FAILED (260ms)

 ERRORS

test => ./main_test.ts:40:6
error: Leaks detected:
  - A signal listener was created during the test, but not fired/cleared during the test. Clear the signal listener by calling `Deno.removeSignalListener`.
  - An async operation to get the next signal was started in this test, but never completed. This is often caused by not un-registering a OS signal handler. The operation was started here:
    at op_signal_poll (ext:core/00_infra.js:250:13)
    at pollSignal (ext:runtime/40_signals.js:18:19)
    at loop (ext:runtime/40_signals.js:73:15)
    at Object.addSignalListener (ext:runtime/40_signals.js:55:5)
    at Process.on (node:process:379:12)
    at Server.init (file:///home/user/.cache/deno/npm/registry.npmjs.org/@mocks-server/core/4.0.2/src/server/Server.js:178:13)
    at Core.init (file:///home/user/.cache/deno/npm/registry.npmjs.org/@mocks-server/core/4.0.2/src/Core.js:203:24)
    at async Core.start (file:///home/user/.cache/deno/npm/registry.npmjs.org/@mocks-server/core/4.0.2/src/Core.js:209:5)
    at async initMockServer (file:///home/user/dev/temp/main_test.ts:6:5)
    at async file:///home/user/dev/temp/main_test.ts:41:20

 FAILURES

test => ./main_test.ts:40:6

FAILED | 0 passed | 1 failed (279ms)

error: Test failed

As you can see in the logs:

  • the server is stopped
  • then the test completes and does the sanitization tests and finds that a signal listener was leaked

Expected behavior
The signal listener should be removed properly when server.stop() is called.

** Operating system, Node.js an npm versions, or browser version (please complete the following information):**

$ deno --version
deno 2.1.6 (stable, release, x86_64-unknown-linux-gnu)
v8 13.0.245.12-rusty
typescript 5.6.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions