Add persistent user and session storage, protected profile requests, and React sign-in controls. Capture and preserve each user's timezone, and load development and production configuration before migrations and server startup.
59 lines
3.3 KiB
TypeScript
59 lines
3.3 KiB
TypeScript
import { afterEach, beforeEach, expect, test } from "bun:test";
|
|
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
let directory: string;
|
|
const project = new URL("../", import.meta.url);
|
|
beforeEach(() => {
|
|
directory = mkdtempSync(join(tmpdir(), "minabot-env-test-"));
|
|
// Run the real package commands with disposable entrypoints that report only fixture values.
|
|
writeFileSync(join(directory, "package.json"), readFileSync(new URL("package.json", project)));
|
|
writeFileSync(join(directory, "bunfig.toml"), readFileSync(new URL("bunfig.toml", project)));
|
|
mkdirSync(join(directory, "scripts"));
|
|
mkdirSync(join(directory, "src"));
|
|
for (const [file, stage] of [["scripts/migrate.ts", "migration"], ["scripts/start.ts", "server"], ["src/index.ts", "server"]]) {
|
|
writeFileSync(join(directory, file!), `console.log(JSON.stringify({ stage: ${JSON.stringify(stage)}, mode: process.env.NODE_ENV, origin: process.env.APP_ORIGIN, database: process.env.DATABASE_PATH, shared: process.env.MINABOT_ENV_TEST_SHARED })); process.exit(0);`);
|
|
}
|
|
writeFileSync(join(directory, ".env"), "APP_ORIGIN=https://shared.example\nDATABASE_PATH=shared.sqlite\nMINABOT_ENV_TEST_SHARED=shared\n");
|
|
writeFileSync(join(directory, ".env.development"), "APP_ORIGIN=http://127.0.0.1:4000\nDATABASE_PATH=development.sqlite\n");
|
|
writeFileSync(join(directory, ".env.production"), "APP_ORIGIN=https://production.example\nDATABASE_PATH=production.sqlite\n");
|
|
});
|
|
afterEach(() => rmSync(directory, { recursive: true, force: true }));
|
|
|
|
function run(command: string, overrides: Record<string, string> = {}) {
|
|
const env = { ...process.env };
|
|
for (const key of ["NODE_ENV", "APP_ORIGIN", "DATABASE_PATH", "MINABOT_ENV_TEST_SHARED", "BUN_OPTIONS"]) delete env[key];
|
|
const result = Bun.spawnSync([process.execPath, "run", command], {
|
|
cwd: directory, env: { ...env, ...overrides }, timeout: 10_000,
|
|
stdout: "pipe", stderr: "pipe",
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
return result.stdout.toString().trim().split("\n").map(line => JSON.parse(line));
|
|
}
|
|
|
|
test.each(["development", "production"])("%s loads the same environment before migration and server startup", mode => {
|
|
const records = run(mode === "production" ? "start" : "dev");
|
|
expect(records).toEqual(["migration", "server"].map(stage => ({
|
|
stage, mode,
|
|
origin: mode === "production" ? "https://production.example" : "http://127.0.0.1:4000",
|
|
database: `${mode}.sqlite`, shared: "shared",
|
|
})));
|
|
});
|
|
|
|
test("database commands select their corresponding environment", () => {
|
|
expect(run("db:migrate")[0].database).toBe("development.sqlite");
|
|
expect(run("db:migrate:production")[0].database).toBe("production.sqlite");
|
|
});
|
|
|
|
test("exported deployment variables override environment files", () => {
|
|
const records = run("start", { APP_ORIGIN: "https://deployed.example", DATABASE_PATH: "deployed.sqlite" });
|
|
expect(records.every(record => record.origin === "https://deployed.example" && record.database === "deployed.sqlite")).toBe(true);
|
|
});
|
|
|
|
test("production never preloads development values when its file is absent", () => {
|
|
rmSync(join(directory, ".env.production"));
|
|
const records = run("start");
|
|
expect(records.every(record => record.origin === "https://shared.example" && record.database === "shared.sqlite")).toBe(true);
|
|
});
|