test(producer): unit tests never reach the network (#5135)

* test(producer): unit tests never reach the network

* test(producer): the unit lane refuses sockets to other hosts, not only fetch

* test(producer): refuse outside hosts at the lookup, not by parsing socket calls

* test(producer): keep dns.lookup promisify-compatible under the no-network guard
This commit is contained in:
Miguel Ángel
2026-10-07 00:52:01 +00:00
committed by GitHub
parent eee0c5101c
commit 04736006b1
6 changed files with 50 additions and 6 deletions
+16
View File
@@ -0,0 +1,16 @@
import dns from "node:dns";
// Unit tests run offline: fetch throws, and a lookup of any host but this machine fails, which also covers a test
// environment's own fetch (happy-dom's) and node:http. A test that needs a response stubs fetch itself.
const LOCAL = new Set(["localhost"]);
const refusal = (target) => new TypeError(`No network in unit tests: ${target}`);
globalThis.fetch = async (input) => {
throw refusal(input?.url ?? input);
};
const { lookup } = dns;
const guarded = (host, ...rest) =>
LOCAL.has(host) ? lookup(host, ...rest) : process.nextTick(rest.at(-1), refusal(host));
// Keeps the original's own properties, among them what util.promisify reads to resolve { address, family }.
dns.lookup = Object.defineProperties(guarded, Object.getOwnPropertyDescriptors(lookup));
+11 -4
View File
@@ -1,4 +1,5 @@
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { discoverProducerTests, PRODUCER_ROOT } from "./test-classification.mjs";
const lane = process.argv[2];
@@ -14,10 +15,13 @@ const tests = discoverProducerTests().filter(
(test) => test.lane === lane && (!requestedRunner || test.runner === requestedRunner),
);
function run(args) {
const noNetwork = new URL("./no-network.mjs", import.meta.url);
const unit = lane === "unit";
function run(args, env = {}) {
const result = spawnSync("bun", args, {
cwd: PRODUCER_ROOT,
env: { ...process.env, HYPERFRAMES_TEST_LANE: lane },
env: { ...process.env, HYPERFRAMES_TEST_LANE: lane, ...env },
stdio: "inherit",
});
if (result.error) throw result.error;
@@ -25,10 +29,13 @@ function run(args) {
}
const vitestFiles = tests.filter((test) => test.runner === "vitest").map((test) => test.file);
if (vitestFiles.length > 0) run(["x", "vitest", "run", ...vitestFiles]);
const vitestEnv = unit
? { NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ""} --import ${noNetwork.href}`.trim() }
: {};
if (vitestFiles.length > 0) run(["x", "vitest", "run", ...vitestFiles], vitestEnv);
// Bun's mock.module registry is process-global. Run each file in a fresh
// process so mocks from one source test cannot mutate another test's imports.
for (const test of tests.filter((entry) => entry.runner === "bun")) {
run(["test", test.file]);
run(["test", ...(unit ? ["--preload", fileURLToPath(noNetwork)] : []), test.file]);
}
+6
View File
@@ -0,0 +1,6 @@
import { expect, it } from "bun:test";
// scripts/run-test-lane.mjs preloads scripts/no-network.mjs for the unit lane.
it("reaches no network in the unit lane", async () => {
await expect(fetch("https://example.com")).rejects.toThrow("No network in unit tests");
});
@@ -0,0 +1,15 @@
// @vitest-environment happy-dom
import { get } from "node:http";
import { expect, it } from "vitest";
// scripts/run-test-lane.mjs imports scripts/no-network.mjs into the unit lane's vitest workers.
it("refuses happy-dom's own fetch in the unit lane", async () => {
await expect(fetch("https://example.com")).rejects.toThrow("No network in unit tests");
});
it("refuses a node:http request to another host in the unit lane", async () => {
const request = new Promise((resolve, reject) => {
get("http://example.com", resolve).on("error", reject);
});
await expect(request).rejects.toThrow("No network in unit tests");
});
@@ -1686,7 +1686,7 @@ describe("crossorigin attribute stripping", () => {
//
// Tests run on localizeRemoteMediaSources directly (exported for testing) to
// avoid invoking ffprobe / the full compileForRender pipeline. fetch is patched
// in-process for success cases; real 404s from example.com cover fallback.
// in-process for success cases; the unit lane's refused network covers fallback.
describe("localizeRemoteMediaSources", () => {
it("rewrites remote <video> src to _remote_media path when download succeeds", async () => {
+1 -1
View File
@@ -60,7 +60,7 @@
"packages/producer/tests/**/*.test.ts"
],
"sources": {
"packages/producer/scripts/run-test-lane.mjs": "bff02680f19eebcce0df72cdeedbbc05edfdcede3f3f60658fc0cb18ed8c59f6",
"packages/producer/scripts/run-test-lane.mjs": "638885e14480d03290803389325a655f3749442167d784e07bb3af3404598af7",
"packages/producer/scripts/test-classification.mjs": "ae0cd9e09bf93df932d41649855f86ebb2230b25aabf3e56355fed8f101a0322",
".github/workflows/ci.yml#job#producer-source-tests": "45da72aa6cf08cdfd5bcf5311e89f12af4842f9eebc6735df19da8c02f4d4864",
"packages/producer/package.json#script#test:unit": "ae6beda8319d166774be958ea5593cd27dbf7f6c9bce1d787a3fe28dfc3477b1",