Something went wrong. Try again.
Something went wrong. Try again.
5.0 kB · 75 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";import { tmpdir } from "node:os";import { join } from "node:path";import { afterEach, beforeEach, describe, expect, it } from "vitest";import { checkEnv, isolateCheck } from "../../runner/checkIsolation";import { runCheck } from "../../runner/checks";
/** P7-5.d: a check runs in its own process and that process does not receive the runner's secrets. Real processes. */
const TOKEN = "glpat-abcdefghij0123456789"; // aite:allow-secretlet dir = "";let checks = "";let before: Record<string, string | undefined> = {};const put = (name: string, text: string) => writeFileSync(join(checks, `${name}.ts`), text);const run = (name: string, over: Partial<Parameters<typeof runCheck>[0]> = {}) => runCheck({ name, checksDir: checks, args: ["a1"], vars: { v: "1" }, timeoutMs: 20_000, read: async () => "page text", url: async () => "http://x/", profile: "p", account: "alice.test", load: async () => ({}), exists: () => true, isolate: isolateCheck, cwd: dir, ...over });
beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "aite-iso-")); checks = join(dir, "scripts/checks"); mkdirSync(checks, { recursive: true }); before = { t: process.env.AITE_GITLAB_TOKEN, p: process.env.AITE_PROFILE_DIR }; process.env.AITE_GITLAB_TOKEN = TOKEN; process.env.AITE_PROFILE_DIR = "/home/someone/profiles/alice";});afterEach(() => { for (const [k, v] of [["AITE_GITLAB_TOKEN", before.t], ["AITE_PROFILE_DIR", before.p]] as const) { if (v === undefined) delete process.env[k]; else process.env[k] = v; } rmSync(dir, { recursive: true, force: true });});
describe("checkEnv", () => { it("keeps a few harmless variables and nothing else", () => { expect(checkEnv({ PATH: "/bin", LANG: "C", AITE_GITLAB_TOKEN: TOKEN, HOME: "/home/x", SECRET: "s" })).toEqual({ PATH: "/bin", LANG: "C" }); expect(checkEnv({})).toEqual({}); });});
describe("a check in its own process", () => { it("does not see AITE_GITLAB_TOKEN, the profile folder, or anything else of the runner's environment", async () => { put("env", `export default () => ({ ok: Object.keys(process.env).every((k) => ["PATH","LANG","LC_ALL","TZ","TMPDIR"].includes(k)), message: "env: " + Object.keys(process.env).join(",") });`); expect(process.env.AITE_GITLAB_TOKEN).toBe(TOKEN); expect(await run("env")).toEqual({ ok: true }); put("token", `export default () => ({ ok: !JSON.stringify(process.env).includes("glpat") && process.env.AITE_GITLAB_TOKEN === undefined && !JSON.stringify(process.env).includes("profiles/alice") });`); expect(await run("token")).toEqual({ ok: true }); }); it("gets its arguments, variables, profile and account, can read the page and ask for the address, and its result decides", async () => { put("full", `export default async (c) => ({ ok: c.args[0] === "a1" && c.vars.v === "1" && c.profile === "p" && c.account === "alice.test" && (await c.read({ css: "x" }, { blocks: true })) === "page text" && (await c.url()) === "http://x/" });`); expect(await run("full")).toEqual({ ok: true }); put("bad", `export default () => ({ ok: false, message: "balance is 4", expected: 5, found: 4 });`); expect(await run("bad")).toMatchObject({ ok: false, reason: "mismatch", message: "balance is 4", expected: 5, found: 4 }); }); it("a read that fails in the runner reaches the check as an error it can see", async () => { put("fail", `export default async (c) => { try { await c.read({ css: "x" }); return false; } catch (e) { return e.message === "not-found"; } };`); expect(await run("fail", { read: async () => { throw new Error("not-found"); } })).toEqual({ ok: true }); }); it("an assertion error, a pre-state error and a crash are told apart", async () => { put("assert", `import assert from "node:assert"; export default () => assert.equal(1, 2);`); expect(await run("assert")).toMatchObject({ ok: false, reason: "mismatch" }); put("pre", `export default () => { const e = new Error("balance 1 is below 5"); e.name = "PreStateFailed"; throw e; };`); expect(await run("pre")).toMatchObject({ ok: false, reason: "pre-state", message: "balance 1 is below 5" }); put("boom", `export default () => { throw new TypeError("x is not a function"); };`); expect(await run("boom")).toMatchObject({ ok: false, reason: "internal" }); put("nofn", `export const other = 1;`); expect(await run("nofn")).toMatchObject({ ok: false, reason: "internal" }); }); it("is stopped when it takes too long, and its process is gone", async () => { put("hang", `export default () => new Promise(() => setInterval(() => {}, 1000));`); const r = await run("hang", { timeoutMs: 1500 }); expect(r).toMatchObject({ ok: false, reason: "timeout" }); }); it("a check that exits without answering is an error, not a pass", async () => { put("quit", `export default () => { process.exit(0); };`); expect(await run("quit")).toMatchObject({ ok: false, reason: "internal" }); });});