150 lines
4.1 KiB
TypeScript
150 lines
4.1 KiB
TypeScript
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import { Hono } from "hono";
|
|
import { Kysely, SqliteDialect } from "kysely";
|
|
import SqliteDatabase from "better-sqlite3";
|
|
import type { db } from "dbtype";
|
|
import { createSettingsRouter } from "../src/route/settings.ts";
|
|
import { mapErrorToResponse } from "../src/route/error_handler.ts";
|
|
import { get_setting, refreshSetting } from "../src/SettingConfig.ts";
|
|
import { PERMISSIONS } from "../src/permission/permission.ts";
|
|
import type { AppEnv, AuthStore } from "../src/login.ts";
|
|
|
|
const normalizeError = (error: unknown): Error => {
|
|
if (error instanceof Error) {
|
|
return error;
|
|
}
|
|
if (typeof error === "string") {
|
|
return new Error(error);
|
|
}
|
|
try {
|
|
return new Error(JSON.stringify(error));
|
|
} catch (_err) {
|
|
return new Error("Unknown error");
|
|
}
|
|
};
|
|
|
|
describe("settings router", () => {
|
|
let sqlite: InstanceType<typeof SqliteDatabase>;
|
|
let database: Kysely<db.DB>;
|
|
|
|
beforeAll(async () => {
|
|
process.env.SERVER_HOST = "127.0.0.1";
|
|
process.env.SERVER_PORT = "3000";
|
|
process.env.SERVER_MODE = "development";
|
|
process.env.JWT_SECRET_KEY = "test-secret";
|
|
|
|
sqlite = new SqliteDatabase(":memory:");
|
|
const dialect = new SqliteDialect({ database: sqlite });
|
|
database = new Kysely<db.DB>({ dialect });
|
|
|
|
await database.schema
|
|
.createTable("app_config")
|
|
.addColumn("key", "text", (col) => col.primaryKey())
|
|
.addColumn("value", "text")
|
|
.execute();
|
|
|
|
await refreshSetting(database);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await database.destroy();
|
|
sqlite.close();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await database.deleteFrom("app_config").execute();
|
|
await refreshSetting(database);
|
|
});
|
|
|
|
const createTestApp = (username: string) => {
|
|
const app = new Hono<AppEnv>();
|
|
const auth: AuthStore = {
|
|
user: { username, permission: [] },
|
|
refreshed: false,
|
|
authenticated: true,
|
|
};
|
|
|
|
app.use("*", async (c, next) => {
|
|
c.set("auth", auth);
|
|
await next();
|
|
});
|
|
|
|
app.onError((err) => {
|
|
const { status, body } = mapErrorToResponse(normalizeError(err));
|
|
return new Response(JSON.stringify(body), {
|
|
status,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
});
|
|
|
|
app.route("/", createSettingsRouter(database));
|
|
|
|
return app;
|
|
};
|
|
|
|
it("rejects access for non-admin users", async () => {
|
|
const app = createTestApp("guest");
|
|
const response = await app.fetch(new Request("http://localhost/settings"));
|
|
expect(response.status).toBe(403);
|
|
});
|
|
|
|
it("returns current configuration for admin", async () => {
|
|
const app = createTestApp("admin");
|
|
const response = await app.fetch(new Request("http://localhost/settings"));
|
|
expect(response.status).toBe(200);
|
|
|
|
const payload = await response.json();
|
|
const expected = get_setting();
|
|
expect(payload).toMatchObject({
|
|
persisted: {
|
|
secure: expected.secure,
|
|
cli: expected.cli,
|
|
forbid_remote_admin_login: expected.forbid_remote_admin_login,
|
|
guest: expected.guest,
|
|
},
|
|
env: {
|
|
hostname: expected.hostname,
|
|
port: expected.port,
|
|
mode: expected.mode,
|
|
},
|
|
});
|
|
expect(Array.isArray(payload.permissions)).toBe(true);
|
|
expect(new Set(payload.permissions)).toEqual(new Set(PERMISSIONS));
|
|
});
|
|
|
|
it("updates persisted settings and returns the new state", async () => {
|
|
const app = createTestApp("admin");
|
|
const request = new Request("http://localhost/settings", {
|
|
method: "PATCH",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({
|
|
secure: false,
|
|
cli: true,
|
|
guest: ["QueryContent"],
|
|
forbid_remote_admin_login: false,
|
|
}),
|
|
});
|
|
|
|
const response = await app.fetch(request);
|
|
expect(response.status).toBe(200);
|
|
|
|
const payload = await response.json();
|
|
expect(payload.persisted).toEqual({
|
|
secure: false,
|
|
cli: true,
|
|
forbid_remote_admin_login: false,
|
|
guest: ["QueryContent"],
|
|
});
|
|
|
|
// A follow-up GET should reflect the updated values
|
|
const followUp = await app.fetch(new Request("http://localhost/settings"));
|
|
expect(followUp.status).toBe(200);
|
|
const followUpPayload = await followUp.json();
|
|
expect(followUpPayload.persisted).toEqual({
|
|
secure: false,
|
|
cli: true,
|
|
forbid_remote_admin_login: false,
|
|
guest: ["QueryContent"],
|
|
});
|
|
});
|
|
});
|