feat(auth): add Discord OAuth sign-in and user timezones
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.
This commit is contained in:
58
scripts/environment.test.ts
Normal file
58
scripts/environment.test.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user