Files
web_hgdjc_h5/tests/environment.test.mjs
T
2026-11-07 11:18:11 +07:00

311 lines
10 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import {
mkdtemp,
mkdir,
writeFile,
readFile,
realpath,
rm,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import ts from "typescript";
import vm from "node:vm";
import { nextCommand, runNext } from "../scripts/next-env.mjs";
async function fixture(t) {
const root = await mkdtemp(join(tmpdir(), "91-environments-"));
t.after(() => rm(root, { recursive: true, force: true }));
await mkdir(join(root, "config"));
for (const environment of ["development", "test", "production"])
await writeFile(
join(root, "config", environment + ".env"),
`API_BASE_URL=https://${environment}.api.test\nPORT=21030\nNEXT_PUBLIC_TRACKER_APP_ID=1050\n`,
);
return root;
}
test("environment defaults, personal overrides and process values use the reference precedence without changing NODE_ENV semantics", async (t) => {
const root = await fixture(t);
await writeFile(
join(root, "config/test.local.env"),
"API_BASE_URL=https://override.api.test\nPORT=21022\nLOCAL_MARKER=yes\n",
);
const base = nextCommand("test", "dev", [], { root, variables: {} });
assert.equal(base.env.API_BASE_URL, "https://override.api.test");
assert.equal(base.env.PORT, "21022");
assert.equal(base.env.LOCAL_MARKER, "yes");
assert.equal(base.env.APP_ENV, "test");
assert.equal(base.env.NODE_ENV, "development");
assert.equal(base.env.APP_DIST_DIR, ".next-test");
const processOverride = nextCommand("test", "start", [], {
root,
variables: {
API_BASE_URL: "https://process.api.test/",
PORT: "31021",
APP_ENV: "production",
NODE_ENV: "test",
APP_DIST_DIR: "outside",
},
});
assert.equal(processOverride.env.API_BASE_URL, "https://process.api.test");
assert.equal(processOverride.env.PORT, "31021");
assert.equal(processOverride.env.APP_ENV, "test");
assert.equal(processOverride.env.NODE_ENV, "production");
assert.equal(processOverride.env.APP_DIST_DIR, ".next-test");
});
test("development supports legacy .env.local while test and production isolate local overrides", async (t) => {
const root = await fixture(t);
await writeFile(
join(root, ".env.local"),
"API_BASE_URL=https://legacy.api.test\nLEGACY_MARKER=dev-only\n",
);
await writeFile(
join(root, "config/development.local.env"),
"API_BASE_URL=https://development-local.api.test\n",
);
const dev = nextCommand("development", "dev", [], { root, variables: {} });
assert.equal(dev.env.API_BASE_URL, "https://development-local.api.test");
assert.equal(dev.env.LEGACY_MARKER, "dev-only");
for (const environment of ["test", "production"]) {
const result = nextCommand(environment, "build", [], {
root,
variables: {},
});
assert.equal(result.env.API_BASE_URL, `https://${environment}.api.test`);
assert.equal(result.env.LEGACY_MARKER, undefined);
}
});
test("UPSTREAM_API_URL follows profile, local and process precedence while preserving legacy process overrides", async (t) => {
const root = await fixture(t);
await writeFile(
join(root, "config/test.env"),
"UPSTREAM_API_URL=http://127.0.0.1:21111\nPORT=21030\n",
);
await writeFile(
join(root, "config/test.local.env"),
"UPSTREAM_API_URL=http://127.0.0.1:21112/\n",
);
let result = nextCommand("test", "start", [], { root, variables: {} });
assert.equal(result.env.UPSTREAM_API_URL, "http://127.0.0.1:21112");
assert.equal(result.env.API_BASE_URL, result.env.UPSTREAM_API_URL);
result = nextCommand("test", "start", [], {
root,
variables: {
UPSTREAM_API_URL: "https://primary.api.test/",
API_BASE_URL: "https://ignored.api.test",
},
});
assert.equal(result.env.UPSTREAM_API_URL, "https://primary.api.test");
assert.equal(result.env.API_BASE_URL, result.env.UPSTREAM_API_URL);
result = nextCommand("test", "start", [], {
root,
variables: { API_BASE_URL: "https://legacy-process.api.test" },
});
assert.equal(result.env.UPSTREAM_API_URL, "https://legacy-process.api.test");
for (const UPSTREAM_API_URL of [
"",
"javascript:bad",
"https://user:password@api.test",
"https://api.test/?query=1",
"https://api.test/#fragment",
]) {
assert.throws(
() =>
nextCommand("test", "start", [], {
root,
variables: { UPSTREAM_API_URL },
}),
/UPSTREAM_API_URL/,
);
}
});
test("each environment selects its own build directory and supports port/hostname command overrides", async (t) => {
const root = await fixture(t);
for (const [environment, directory] of [
["development", ".next-dev"],
["test", ".next-test"],
["production", ".next"],
]) {
for (const command of ["dev", "build", "start"]) {
const result = nextCommand(environment, command, [], {
root,
variables: {},
});
assert.equal(result.env.APP_DIST_DIR, directory);
assert.equal(result.cwd, root);
assert.equal(
result.env.NODE_ENV,
command === "dev" ? "development" : "production",
);
if (command === "build")
assert.deepEqual(result.args.slice(1), ["build"]);
else
assert.deepEqual(result.args.slice(2), [
"--hostname",
"0.0.0.0",
"--port",
"21030",
]);
}
}
const explicit = nextCommand(
"test",
"dev",
["--port", "31022", "--hostname=127.0.0.1", "--webpack"],
{ root, variables: {} },
);
assert.deepEqual(explicit.args.slice(1), [
"dev",
"--port",
"31022",
"--hostname=127.0.0.1",
"--webpack",
]);
});
test("current-domain profiles preserve runtime resolution instead of requiring a build-time URL", async (t) => {
const root = await fixture(t);
for (const environment of ["test", "production"]) {
await writeFile(
join(root, "config", environment + ".env"),
"UPSTREAM_API_URL=current\nPORT=21030\n",
);
for (const command of ["dev", "build", "start"]) {
const result = nextCommand(environment, command, [], {
root,
variables: {},
});
assert.equal(result.env.UPSTREAM_API_URL, "current");
assert.equal(result.env.API_BASE_URL, "current");
}
const override = nextCommand(environment, "start", [], {
root,
variables: { UPSTREAM_API_URL: "http://127.0.0.1:21111" },
});
assert.equal(override.env.UPSTREAM_API_URL, "http://127.0.0.1:21111");
}
});
test("invalid environment, command, API or port fails before a child process is started", async (t) => {
const root = await fixture(t);
for (const [environment, command] of [
["staging", "dev"],
["../test", "dev"],
["development", "deploy"],
])
assert.throws(
() => nextCommand(environment, command, [], { root, variables: {} }),
/Usage:/,
);
for (const API_BASE_URL of [
"",
"javascript:bad",
"https://name:password@api.test",
"https://api.test/#fragment",
])
assert.throws(
() =>
nextCommand("test", "dev", [], { root, variables: { API_BASE_URL } }),
/API_BASE_URL/,
);
for (const PORT of ["0", "65536", "not-a-port", "-1"])
assert.throws(
() => nextCommand("test", "dev", [], { root, variables: { PORT } }),
/PORT/,
);
await rm(join(root, "config/test.env"));
assert.throws(
() => nextCommand("test", "dev", [], { root, variables: {} }),
/缺少环境配置/,
);
});
test("real child execution receives the selected environment, preserves args and propagates its exit status", async (t) => {
const root = await fixture(t),
bin = join(root, "node_modules/next/dist/bin");
await mkdir(bin, { recursive: true });
const output = join(root, "captured.json");
await writeFile(
join(bin, "next"),
`require('node:fs').writeFileSync(process.env.CAPTURE,JSON.stringify({app:process.env.APP_ENV,node:process.env.NODE_ENV,api:process.env.API_BASE_URL,args:process.argv.slice(2),cwd:process.cwd()}));process.exit(7);`,
);
assert.equal(
await runNext("test", "start", ["--port", "31021"], {
root,
variables: { CAPTURE: output },
}),
7,
);
const saved = JSON.parse(await readFile(output, "utf8"));
assert.deepEqual(saved, {
app: "test",
node: "production",
api: "https://test.api.test",
args: ["start", "--hostname", "0.0.0.0", "--port", "31021"],
cwd: await realpath(root),
});
});
test("project profiles and Next configuration keep the requested API and port with distinct artifacts", async () => {
const source = ts.transpileModule(
await readFile(new URL("../next.config.ts", import.meta.url), "utf8"),
{
compilerOptions: {
target: ts.ScriptTarget.ES2022,
module: ts.ModuleKind.CommonJS,
},
},
).outputText;
for (const [environment, directory] of [
["development", ".next-dev"],
["test", ".next-test"],
["production", ".next"],
]) {
const command = nextCommand(environment, "build", [], { variables: {} }),
exports = {};
vm.runInNewContext(source, {
exports,
process: { env: command.env, cwd: () => process.cwd() },
});
assert.equal(exports.default.distDir, directory);
assert.equal(
command.env.UPSTREAM_API_URL,
environment === "production"
? "https://webhgcapi.mfcg.me"
: "https://webhgcapi.mfcg.me",
);
assert.equal(command.env.API_BASE_URL, command.env.UPSTREAM_API_URL);
assert.equal(
command.env.PORT,
environment === "production" ? "21042" : "21030",
);
assert.equal(command.env.NEXT_PUBLIC_TRACKER_APP_ID, undefined);
const ignored = spawnSync(
"git",
["check-ignore", "--no-index", `config/${environment}.local.env`],
{ encoding: "utf8" },
);
assert.equal(ignored.status, 0);
}
const pkg = JSON.parse(
await readFile(new URL("../package.json", import.meta.url), "utf8"),
);
for (const name of [
"dev",
"dev:test",
"build",
"build:dev",
"build:test",
"start",
"start:dev",
"start:test",
])
assert.match(pkg.scripts[name], /next-env\.mjs/);
});