Test sanitizers
The ende test runner offers several sanitizers
that catch tests misbehaving in ways assertions don't see: leaking async
operations, unclosed resources, and unexpected process exits.
Resource sanitizer
The resource sanitizer ensures that all I/O resources created during a test are closed, to prevent leaks.
I/O resources are things like Ende.FsFile handles,
network connections, fetch bodies, timers, and other
resources that are not automatically garbage collected.
You should always close resources when you are done with them. For example, to close a file:
const file = await Ende.open("hello.txt");
// Do something with the file
file.close(); // <- Always close the file when you are done with it
To close a network connection:
const conn = await Ende.connect({ hostname: "example.com", port: 80 });
// Do something with the connection
conn.close(); // <- Always close the connection when you are done with it
To close a fetch body:
const response = await fetch("https://example.com");
// Do something with the response
await response.body?.cancel(); // <- Always cancel the body when you are done with it, if you didn't consume it otherwise
This sanitizer is off by default. Opt in with sanitizeResources: true, or
with one of the global mechanisms described in
Enabling sanitizers globally.
Ende.test({
name: "no leaks allowed",
async fn() {
using file = await Ende.open("hello.txt");
// ...
},
sanitizeResources: true
});
Async operation sanitizer
The async operation sanitizer ensures that all async operations started in a test are completed before the test ends. This is important because if an async operation is not awaited, the test will end before the operation is completed, and the test will be marked as successful even if the operation may have actually failed.
You should always await all async operations in your tests. For example:
Ende.test({
name: "async operation test",
async fn() {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
});
This sanitizer is off by default. Opt in with sanitizeOps: true, or with
one of the global mechanisms described below.
Ende.test({
name: "no leaked ops allowed",
async fn() {
await someAsyncWork();
},
sanitizeOps: true
});
Enabling sanitizers globally
If you want the pre-2.8 behavior (resource and op sanitizers on for every test), you can re-enable them at any of five scopes. Higher-precedence settings override lower ones.
-
Per-test (highest precedence):
Ende.test({ name: "strict", sanitizeOps: true, sanitizeResources: true, fn() {/* … */} }); -
Per-module with
Ende.test.sanitizer():Ende.test.sanitizer({ ops: true, resources: true }); Ende.test("uses module-level sanitizers", () => {/* … */}); -
CLI flags:
--sanitize-opsand--sanitize-resources. -
Environment variables:
ENDE_TEST_SANITIZE_OPS=1andENDE_TEST_SANITIZE_RESOURCES=1. -
ende.json(lowest precedence):{ "test": { "sanitizeOps": true, "sanitizeResources": true } }
Exit sanitizer
The exit sanitizer ensures that tested code doesn’t call
Ende.exit(), which could signal a false test
success.
This sanitizer is enabled by default, but can be disabled with
sanitizeExit: false.
Ende.test({
name: "false success",
fn() {
Ende.exit(0);
},
sanitizeExit: false
});
// This test never runs, because the process exits during "false success" test
Ende.test({
name: "failing test",
fn() {
throw new Error("this test fails");
}
});