Repository navigation
Oak leaking async ops on simple server test #454
Copy link
Copy link
Open
Description
When trying to use oak to unit test some REST services, the server seems to leak async ops. Simplest code to reproduce below. Would love to know if I am doing something wrong (maybe explicit close connection?) or if this is a bug. Thanks much.
I run the test below using:
deno test -A server.test.ts Sample code:
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
const PORT = 8080;
let listenPromise: Promise<void>;
const controller = new AbortController();
// Starts server and saves the returned promise into `listenPromise`
export const startServer = function(port: number): void {
const app = new Application(), router = new Router();
// Ping route returns "PONG""
router.get("/ping", (ctx) => {
ctx.response.body = "PONG";
ctx.response.status = 200;
});
app.use(router.routes());
app.use(router.allowedMethods());
listenPromise = app.listen({ port: port, signal: controller.signal });
};
export const stopServer = async function(): Promise<void> {
controller.abort();
await listenPromise;
};
// starts small server defined above
startServer(PORT);
// BUG: THIS TEST CASE IS LEAKING ASYNC OPS, WHY?
Deno.test("test that I can connect to server", async function() {
const url = "http://localhost:8080/ping";
const response = await fetch(url);
const text = await response.text();
assertEquals(text, "PONG");
});
// Close the server on a test so that it runs at the end
Deno.test("close server", { sanitizeOps: false, sanitizeResources: false }, async function() {
await stopServer();
});The console output looks like this:
running 2 tests from file:///Users/andres/Code/RC/gateways/test/bug.test.ts
test test that I can connect to server ... FAILED (7ms)
test close server ... ok (1ms)
failures:
test that I can connect to server
AssertionError: Test case is leaking async ops.
Before:
- dispatched: 2
- completed: 1
After:
- dispatched: 10
- completed: 8
Ops:
op_http_accept:
Before:
- dispatched: 0
- completed: 0
After:
- dispatched: 2
- completed: 1
Make sure to await all promises returned from Deno APIs before
finishing test case.
at assert (deno:runtime/js/06_util.js:41:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:121:7)
at async resourceSanitizer (deno:runtime/js/40_testing.js:137:7)
at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:169:9)
at async runTest (deno:runtime/js/40_testing.js:427:7)
at async Object.runTests (deno:runtime/js/40_testing.js:540:22)
failures:
test that I can connect to server
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (29ms)
error: Test failed
Metadata
Metadata
Assignees
Labels
No labels
Activity
acrodrig commentedon Jan 24, 2022
I should add that if I replace URL
http://localhost:8080/pingin the test case for an external URL such ashttps://filesamples.com/samples/code/json/sample2.jsonthen the test no longer leaks, so presumably the leak is happening at therouter.get"ping" function.cmorten commentedon Aug 29, 2022
In this instance got a hunch it is your setup that's leaking based on the error.
op_http_acceptas0dispatched prior to the test. This indicates that although you have calledstartServer()prior to the test, Oak still hasn't managed to make it's firstlistener.accept()call yet.2accept ops have been dispatched but only1has been completed. The completed one will be for the very firstlistener.accept()promise which will have completed when you made thefetch()call to the Oak server. The second accept op which we can see is dispatched but not completed is because under the hood Oak automatically looks to accept another connection but with no further requests (or stopping the server) this promise will never resolve.E.g. the following adjustment to your test file doesn't throw:
There may be a way to setup the tests to avoid having the
startServer()andstopServer()calls in each of your tests. For this would likely need to make use of the"listen"event so you can await the firstaccept()call being made reliably.See https://github.com/oakserver/oak#opening-the-server
E.g. in addition to your
listenPromisehave it's "listening" counterpart...gonzus commentedon Jul 15, 2025
Hello. Apologies for replying to an issue after almost three years.
I am seeing this same symptom with Deno tests that start up a server and then try to stop it:
"http" was created during the test, but not cleaned up during the test. I copied your example above verbatim (without the "listening" counterpart) and ran it, same issue. This is with deno2.4.1. Full output (running with--trace-leaks) below. Any ideas?gonzus commentedon Jul 15, 2025
Also (unrelated), I have been seeing this warning for a couple of weeks / deno versions now:
oak send() does not work under Node.js. There does not seem to be a way to turn that off. Is this intended, or a regression? I am not usingsend()anywhere.thehenrytsai commentedon Jul 16, 2025
@gonzus, I ran into the exact same problem after upgrading to Deno v.2.4.1 with my tests.
After various failed attempts, I upgraded Oak from v17.1.4 to v17.1.5 which made the errors go away!
gonzus commentedon Jul 17, 2025
Thanks for the hint @thehenrytsai -- upgrading Oak seems to have fixed the test for me, but with some minor changes. I am pasting my full test below, for posterity.
which produces the following output: