Refactor Study Desk application structure
This commit is contained in:
parent
faaccf8a7e
commit
089439ed90
145 changed files with 8087 additions and 3412 deletions
83
scripts/verify-http-smoke.mjs
Normal file
83
scripts/verify-http-smoke.mjs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { randomUUID } from "node:crypto";
|
||||
import { mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { spawn } from "node:child_process";
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
const root = path.join(process.cwd(), ".test-databases");
|
||||
const directory = path.join(root, `http-smoke-${randomUUID()}`);
|
||||
if (!path.resolve(directory).startsWith(`${path.resolve(root)}${path.sep}`)) {
|
||||
throw new Error("Unsafe disposable HTTP smoke path");
|
||||
}
|
||||
mkdirSync(directory, { recursive: true });
|
||||
const databasePath = path.join(directory, "http-smoke.test.db");
|
||||
const database = new Database(databasePath);
|
||||
try {
|
||||
database.pragma("foreign_keys = ON");
|
||||
for (const migration of readdirSync(path.join(process.cwd(), "prisma", "migrations"), { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => entry.name)
|
||||
.sort()) {
|
||||
database.exec(readFileSync(path.join(process.cwd(), "prisma", "migrations", migration, "migration.sql"), "utf8"));
|
||||
}
|
||||
} finally {
|
||||
database.close();
|
||||
}
|
||||
|
||||
const port = 3789;
|
||||
const server = spawn(
|
||||
process.execPath,
|
||||
[path.join(process.cwd(), "node_modules", "next", "dist", "bin", "next"), "start", "-p", String(port)],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "production",
|
||||
DATABASE_URL: `file:${databasePath.replaceAll("\\", "/")}`,
|
||||
SESSION_SECRET: "http-smoke-secret-0123456789abcdef0123456789",
|
||||
ALLOW_INITIAL_SETUP: "true",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
windowsHide: true,
|
||||
}
|
||||
);
|
||||
let output = "";
|
||||
server.stdout.on("data", (chunk) => { output += chunk.toString(); });
|
||||
server.stderr.on("data", (chunk) => { output += chunk.toString(); });
|
||||
|
||||
try {
|
||||
let health;
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
try {
|
||||
health = await fetch(`http://127.0.0.1:${port}/api/health`);
|
||||
if (health.ok) break;
|
||||
} catch {}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
if (!health?.ok) throw new Error(`Health check did not become ready.\n${output}`);
|
||||
const login = await fetch(`http://127.0.0.1:${port}/login`);
|
||||
const setup = await fetch(`http://127.0.0.1:${port}/api/auth/setup-status`);
|
||||
const setupBody = await setup.json();
|
||||
const protectedDot = await fetch(`http://127.0.0.1:${port}/api/decks/file.json`, { redirect: "manual" });
|
||||
if (!login.ok || !setup.ok || setupBody.setupRequired !== true || setupBody.setupAllowed !== true || protectedDot.status !== 307) {
|
||||
throw new Error("HTTP smoke responses did not match the production contract");
|
||||
}
|
||||
console.info(JSON.stringify({
|
||||
health: health.status,
|
||||
login: login.status,
|
||||
setup: setupBody,
|
||||
protectedDot: { status: protectedDot.status, location: protectedDot.headers.get("location") },
|
||||
}, null, 2));
|
||||
if (process.argv.includes("--stay")) {
|
||||
console.info(`Browser smoke server ready at http://127.0.0.1:${port}/login`);
|
||||
await new Promise(() => {});
|
||||
}
|
||||
} finally {
|
||||
server.kill();
|
||||
await new Promise((resolve) => {
|
||||
if (server.exitCode !== null) return resolve();
|
||||
server.once("exit", resolve);
|
||||
setTimeout(resolve, 2_000);
|
||||
});
|
||||
rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue