158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { spawnSync } from "node:child_process";
|
|
import {
|
|
mkdtempSync,
|
|
mkdirSync,
|
|
readFileSync,
|
|
rmSync,
|
|
writeFileSync,
|
|
existsSync,
|
|
} from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { certificateFixture } from "../certificate-fixture";
|
|
|
|
// Execute the generated shell in a disposable filesystem. Only systemctl and
|
|
// network effects are mocked; bash, jq, openssl, permissions and file writes run.
|
|
for (const scenario of [
|
|
"success",
|
|
"health-failure",
|
|
"occupied",
|
|
"bad-domain",
|
|
]) {
|
|
test(`installer shell: ${scenario}`, () => {
|
|
const dir = mkdtempSync(path.join(tmpdir(), "hy2-shell-test-"));
|
|
const fake = path.join(dir, "bin");
|
|
mkdirSync(fake);
|
|
mkdirSync(path.join(dir, "systemd"));
|
|
const executable = (name: string, body: string) =>
|
|
writeFileSync(path.join(fake, name), `#!/usr/bin/env bash\n${body}\n`, {
|
|
mode: 0o700,
|
|
});
|
|
const fixture = certificateFixture();
|
|
writeFileSync(
|
|
path.join(dir, "bundle.json"),
|
|
JSON.stringify({
|
|
...fixture,
|
|
config: "listen: :443\n",
|
|
statsSecret: "test-stats",
|
|
}),
|
|
);
|
|
executable("id", "echo 0");
|
|
executable("uname", "echo x86_64");
|
|
executable("sleep", "exit 0");
|
|
executable(
|
|
"systemctl",
|
|
`echo "$*" >> "$SANDBOX/systemctl.log"
|
|
if [ "$1" = is-active ]; then
|
|
[ "$3" = hy2-panel-node.service ] && [ "$SCENARIO" != health-failure ]; exit $?
|
|
fi
|
|
exit 0`,
|
|
);
|
|
executable(
|
|
"curl",
|
|
`url=""; output=""; request=""
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
-o) output=$2; shift 2;;
|
|
--data-binary) request=$2; shift 2;;
|
|
http*) url=$1; shift;;
|
|
*) shift;;
|
|
esac
|
|
done
|
|
case "$url" in
|
|
https://download.hysteria.network/*) printf '#!/usr/bin/env bash\\nexit 0\\n' > "$output";;
|
|
*/api/install)
|
|
cp "\${request#@}" "$SANDBOX/last-request.json"
|
|
if jq -e '.action == "prepare"' "\${request#@}" >/dev/null; then
|
|
cp "\${request#@}" "$SANDBOX/prepare.json"
|
|
cp "$SANDBOX/bundle.json" "$output"
|
|
else echo '{"ok":true}' > "$output"; touch "$SANDBOX/completed"; fi
|
|
printf 200;;
|
|
*/traffic) [ "$SCENARIO" != health-failure ];;
|
|
*) exit 1;;
|
|
esac`,
|
|
);
|
|
const template = readFileSync("lib/templates/install-node.sh", "utf8")
|
|
.replaceAll("__PANEL__", "'https://panel.example'")
|
|
.replaceAll("__TOKEN__", "'test-token'")
|
|
.replaceAll("__INSTALL_ID__", "'test-job'")
|
|
.replaceAll("__SLUG__", "'test-node'")
|
|
.replaceAll("/run/systemd/system", path.join(dir, "systemd"))
|
|
.replaceAll("/etc/hysteria", path.join(dir, "hysteria"))
|
|
.replaceAll("/etc/systemd/system", path.join(dir, "systemd"))
|
|
.replaceAll("/usr/local/lib/hy2-panel", path.join(dir, "lib"));
|
|
const script = path.join(dir, "install.sh");
|
|
writeFileSync(script, template);
|
|
if (scenario === "occupied") {
|
|
mkdirSync(path.join(dir, "hysteria"));
|
|
writeFileSync(path.join(dir, "hysteria/config.yaml"), "original-config");
|
|
}
|
|
try {
|
|
const result = spawnSync(
|
|
"bash",
|
|
[
|
|
script,
|
|
"--domain",
|
|
scenario === "bad-domain" ? "wrong.example" : "test",
|
|
"--hy2-port",
|
|
"23456",
|
|
"--stats-port",
|
|
"23457",
|
|
"--hy2-listen-port",
|
|
"443",
|
|
"--stats-listen-port",
|
|
"9999",
|
|
],
|
|
{
|
|
encoding: "utf8",
|
|
env: {
|
|
...process.env,
|
|
PATH: `${fake}:${process.env.PATH}`,
|
|
SANDBOX: dir,
|
|
SCENARIO: scenario,
|
|
},
|
|
timeout: 20000,
|
|
},
|
|
);
|
|
assert.equal(
|
|
result.status,
|
|
scenario === "success" ? 0 : 1,
|
|
result.stderr + result.stdout,
|
|
);
|
|
assert.equal(
|
|
existsSync(path.join(dir, "completed")),
|
|
scenario === "success",
|
|
);
|
|
if (scenario === "success") {
|
|
assert.equal(
|
|
readFileSync(path.join(dir, "hysteria/server.key"), "utf8").trim(),
|
|
fixture.privateKey.trim(),
|
|
);
|
|
assert.equal(
|
|
JSON.parse(readFileSync(path.join(dir, "prepare.json"), "utf8")).ports
|
|
.domain,
|
|
"test.p.xn--876a.net",
|
|
);
|
|
assert.match(
|
|
readFileSync(path.join(dir, "systemctl.log"), "utf8"),
|
|
/enable hy2-panel-node/,
|
|
);
|
|
}
|
|
if (scenario === "health-failure")
|
|
assert.match(
|
|
readFileSync(path.join(dir, "systemctl.log"), "utf8"),
|
|
/stop hy2-panel-node.service/,
|
|
);
|
|
if (scenario === "occupied")
|
|
assert.equal(
|
|
readFileSync(path.join(dir, "hysteria/config.yaml"), "utf8"),
|
|
"original-config",
|
|
);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
}
|