Files
Endoleap/tests/api-smoke.mjs
T

115 lines
3.3 KiB
JavaScript

import assert from "node:assert/strict";
import { createServer } from "node:http";
import { spawn } from "node:child_process";
import { once } from "node:events";
import { fileURLToPath } from "node:url";
const host = "http://127.0.0.1:3005";
const upstream = createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
if (req.url.endsWith("/auth/login")) {
res.end(
JSON.stringify({
access_token: "test-only-session",
expires_in: 28800,
token_type: "Bearer",
}),
);
} else if (req.url.endsWith("/profile")) {
res.statusCode =
req.headers.authorization === "Bearer test-only-session" ? 200 : 401;
res.end(
JSON.stringify(
res.statusCode === 200
? { username: "test" }
: { error: "invalid_session" },
),
);
} else {
res.statusCode = 404;
res.end("{}");
}
});
await new Promise((r) => upstream.listen(0, "127.0.0.1", r));
const port = upstream.address().port;
const app = spawn(
process.execPath,
[
"node_modules/next/dist/bin/next",
"start",
"--hostname",
"127.0.0.1",
"--port",
"3005",
],
{
cwd: fileURLToPath(new URL("../", import.meta.url)),
env: { ...process.env, FOUNDRY_API_ORIGIN: "http://127.0.0.1:" + port },
stdio: ["ignore", "pipe", "pipe"],
},
);
let startup = "";
app.stdout.on("data", (x) => (startup += x));
app.stderr.on("data", (x) => (startup += x));
try {
let ready = false;
for (let n = 0; n < 100; n++) {
if (app.exitCode !== null)
throw new Error("Frontend failed to start: " + startup);
try {
if ((await fetch(host)).ok) {
ready = true;
break;
}
} catch {}
await new Promise((r) => setTimeout(r, 100));
}
assert(ready, "Frontend did not start");
const res = await fetch(host + "/api/foundry/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json", Origin: host },
body: "{}",
});
assert.equal(res.status, 200);
assert.match(res.headers.get("server-timing") || "", /upstream;dur=\d/);
const body = await res.json();
assert.equal(body.access_token, undefined);
const cookie = res.headers.get("set-cookie");
assert(cookie?.includes("HttpOnly"));
assert(cookie.includes("SameSite=Lax"));
assert(cookie.includes("Max-Age=28800"));
const session = cookie.split(";")[0];
assert.equal(
(
await fetch(host + "/api/foundry/profile", {
headers: { Cookie: session },
})
).status,
200,
);
assert.equal((await fetch(host + "/api/foundry/profile")).status, 401);
for (const origin of ["https://example.com", "null"])
assert.equal(
(
await fetch(host + "/api/foundry/auth/login", {
method: "POST",
headers: { Origin: origin, "Content-Type": "application/json" },
body: "{}",
})
).status,
403,
);
assert.equal((await fetch(host + "/api/foundry/unlisted")).status, 404);
assert.equal(
(await fetch(host + "/api/oauth/slack?challenge=bad")).status,
400,
);
console.log(
"PASS: session cookie, token removal, authenticated proxy, invalid session, cross-origin rejection, path allowlist, OAuth validation",
);
} finally {
const exited = once(app, "exit");
app.kill("SIGTERM");
await exited;
await new Promise((r) => upstream.close(r));
}