151 lines
4.9 KiB
TypeScript
151 lines
4.9 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { readdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
|
|
import { expect, it } from "vitest";
|
|
|
|
import {
|
|
nativeAotCommandError,
|
|
supportsNativeAotFixtureHost,
|
|
} from "../../../scripts/lib/nativeaot-build.mjs";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
const generator = fileURLToPath(
|
|
new URL("../../../scripts/generate-nativeaot-fixtures.mjs", import.meta.url),
|
|
);
|
|
|
|
const supportedHost = supportsNativeAotFixtureHost(
|
|
process.platform,
|
|
process.arch,
|
|
);
|
|
|
|
it.skipIf(!supportedHost).each(["restore", "publish"])(
|
|
"redacts authenticated feed URLs from the uncaught %s diagnostic",
|
|
async (operation) => {
|
|
const directory = await createTestTempDirectory(
|
|
"rea-nativeaot-diagnostic-",
|
|
);
|
|
const hook = join(directory, "child-output.cjs");
|
|
await writeFile(
|
|
hook,
|
|
`
|
|
const child = require("node:child_process");
|
|
child.spawnSync = (_command, args) => {
|
|
if (args[0] === "--version") return { status: 0, stdout: "8.0.416\\n", stderr: "" };
|
|
if (args[0] === ${JSON.stringify(operation)}) return {
|
|
status: 1,
|
|
error: new Error("failed HTTPS://error-user:error-secret@feed.invalid/v3/index.json"),
|
|
stdout: "NU1301 https://stdout-user:stdout-secret@feed.invalid/v3/index.json\\n",
|
|
stderr: "source https://stderr-user:stderr-secret%40suffix@feed.invalid/v3/index.json\\nkeep https://feed.invalid/path/user@name?label=token-name\\n"
|
|
};
|
|
return { status: 0, stdout: "", stderr: "" };
|
|
};
|
|
require("node:module").syncBuiltinESMExports();
|
|
`,
|
|
"utf8",
|
|
);
|
|
const failure = await promisify(execFile)(
|
|
process.execPath,
|
|
["--require", hook, generator],
|
|
{
|
|
env: {
|
|
...process.env,
|
|
REA_NATIVEAOT_FIXTURE_ROOT: join(directory, "output"),
|
|
},
|
|
timeout: 15_000,
|
|
},
|
|
).then(
|
|
() => {
|
|
throw new Error("Expected fixture generation to fail");
|
|
},
|
|
(error: { stderr: string; code: number }) => error,
|
|
);
|
|
expect(failure.code).toBe(1);
|
|
for (const secret of [
|
|
"error-user",
|
|
"error-secret",
|
|
"stdout-user",
|
|
"stdout-secret",
|
|
"stderr-user",
|
|
"stderr-secret",
|
|
])
|
|
expect(failure.stderr).not.toContain(secret);
|
|
expect(failure.stderr).toContain("NU1301");
|
|
expect(failure.stderr).toContain("feed.invalid/v3/index.json");
|
|
expect(failure.stderr).toContain(
|
|
"https://feed.invalid/path/user@name?label=token-name",
|
|
);
|
|
if (operation === "restore") {
|
|
expect(failure.stderr).toContain("attempt 2/2");
|
|
expect(failure.stderr).toContain("failed after two attempts");
|
|
expect(failure.stderr).toContain("[cause]");
|
|
}
|
|
},
|
|
);
|
|
|
|
it.skipIf(supportedHost)(
|
|
"refuses unsupported fixture hosts before tooling or output creation",
|
|
async () => {
|
|
const directory = await createTestTempDirectory("rea-nativeaot-host-");
|
|
const hook = join(directory, "unexpected-child.cjs");
|
|
await writeFile(
|
|
hook,
|
|
`const child = require("node:child_process");
|
|
child.spawnSync = () => { throw new Error("Unexpected fixture tooling invocation"); };
|
|
require("node:module").syncBuiltinESMExports();
|
|
`,
|
|
"utf8",
|
|
);
|
|
const failure = await promisify(execFile)(
|
|
process.execPath,
|
|
["--require", hook, generator],
|
|
{
|
|
env: {
|
|
...process.env,
|
|
REA_NATIVEAOT_FIXTURE_ROOT: join(directory, "output"),
|
|
},
|
|
timeout: 15_000,
|
|
},
|
|
).then(
|
|
() => {
|
|
throw new Error("Expected unsupported fixture host refusal");
|
|
},
|
|
(error: { stderr: string; code: number }) => error,
|
|
);
|
|
expect(failure.code).toBe(1);
|
|
expect(failure.stderr).toContain("requires Linux or Windows x64");
|
|
expect(failure.stderr).not.toContain(
|
|
"Unexpected fixture tooling invocation",
|
|
);
|
|
expect(await readdir(directory)).toEqual(["unexpected-child.cjs"]);
|
|
},
|
|
);
|
|
|
|
it("preserves diagnostics without transport authentication exactly", () => {
|
|
const error = nativeAotCommandError(
|
|
"C:/token projects/dotnet.exe",
|
|
"restore",
|
|
{
|
|
status: 1,
|
|
stdout: "NU1301 https://feed.invalid/user@name?label=password\n",
|
|
stderr: "project C:/token projects/fixture.csproj unavailable",
|
|
},
|
|
);
|
|
expect(error.message).toBe(
|
|
"NativeAOT fixture lane requires/action failed C:/token projects/dotnet.exe restore: exit 1\nNU1301 https://feed.invalid/user@name?label=password\nproject C:/token projects/fixture.csproj unavailable",
|
|
);
|
|
expect(error.cause).toBeUndefined();
|
|
});
|
|
|
|
it("treats HTTP backslashes as path separators rather than authentication", () => {
|
|
const output = String.raw`https://feed.invalid\path\user@name`;
|
|
expect(new URL(output).username).toBe("");
|
|
const error = nativeAotCommandError("dotnet", "restore", {
|
|
status: 1,
|
|
stdout: output,
|
|
});
|
|
expect(error.message).toContain(output);
|
|
});
|