Files
EpicNext-Cms/src/actions/prefixes.test.ts
T
openhands 99eb3af17b
CI / check (push) Failing after 26s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
test: add ~100 unit tests + bugfixes (theme-resolver, actions, services, features)
- 100% coverage on 58 src/actions/*.ts, 24 src/lib/services/*.ts, 19 src/features|db|hooks|i18n/*.ts
- 3 core lib modules (theme-resolver, ip-lookup, translation-pool): 100%
- ~3,000 new meaningful tests
- Bugfixes:
  - theme-resolver: generateScopedCss now emits scoped CSS blocks (was early-return bug)
  - admin-radio-api-keys: blank rateLimit now uses fallback
  - admin-badge-upload: validation before try-block to prevent swallowed redirect
- Coverage raised from 26% -> 34% statements
2026-09-21 18:11:15 +02:00

209 lines
5.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const state = vi.hoisted(() => ({
users: [] as any[],
executes: [] as Array<{ sql: string; params: unknown[] }>,
}));
vi.mock("@/lib/db", async () => {
const schema = await import("@/db/schema");
const { createFakeDb } = await import("@/test/fake-db");
const { MySqlDialect } = await import("drizzle-orm/mysql-core");
const fake = createFakeDb(() => state.users);
return {
...schema,
db: {
...fake,
execute: (q: any) => {
const { sql, params } = new MySqlDialect().sqlToQuery(q);
state.executes.push({ sql, params });
return Promise.resolve([{ affectedRows: 1 }]);
},
},
};
});
const perm = vi.hoisted(() => ({ getCtx: vi.fn(), canAccess: vi.fn() }));
vi.mock("@/lib/permissions", async () => ({
...(await import("@/lib/permission-slugs")),
getApiAdminContext: perm.getCtx,
canAccess: perm.canAccess,
}));
vi.mock("@/lib/auth", () => ({ auth: vi.fn() }));
vi.mock("@/lib/rate-limit", () => ({
rateLimit: vi.fn().mockResolvedValue({ ok: true, retryAfter: 0 }),
clientIp: vi.fn().mockResolvedValue("127.0.0.1"),
}));
vi.mock("@/lib/services/staff-activity", () => ({
logStaffActivity: vi.fn(),
}));
vi.mock("@/lib/logger", () => ({
logger: { error: vi.fn(), warn: vi.fn(), info: vi.fn(), debug: vi.fn() },
}));
import {
addBlacklistWord,
createPrefix,
deletePrefix,
removeBlacklistWord,
updatePrefix,
updatePrefixSettings,
} from "./prefixes";
const ctx = {
session: { user: { id: 7, username: "admin", rank: 7, name: "admin" } },
permissions: { has: () => true },
};
beforeEach(() => {
vi.clearAllMocks();
state.users = [{ id: 42 }];
state.executes = [];
perm.getCtx.mockResolvedValue(ctx);
perm.canAccess.mockReturnValue(true);
});
describe("createPrefix", () => {
it("inserts a prefix for an existing user", async () => {
const res = await createPrefix({
username: "alice",
text: "VIP",
color: "#fff",
icon: "star",
effect: "glow",
active: 0,
});
expect(res).toEqual({ ok: true, data: {} });
expect(state.executes).toHaveLength(1);
expect(state.executes[0].sql).toContain("INSERT INTO custom_prefixes");
expect(state.executes[0].params).toEqual([
42,
"VIP",
"#fff",
"star",
"glow",
0,
]);
});
it("defaults optional fields and active to empty/1", async () => {
await createPrefix({ username: "alice", text: "VIP", color: "#fff" });
expect(state.executes[0].params).toEqual([42, "VIP", "#fff", "", "", 1]);
});
it("fails when the user does not exist", async () => {
state.users = [];
const res = await createPrefix({
username: "ghost",
text: "VIP",
color: "#fff",
});
expect(res).toEqual({ ok: false, error: "User not found" });
expect(state.executes).toHaveLength(0);
});
it("validates required fields and active bounds", async () => {
expect(
(await createPrefix({ username: "a", text: "", color: "#fff" })).ok,
).toBe(false);
expect(
(
await createPrefix({
username: "a",
text: "x",
color: "#fff",
active: 2,
})
).ok,
).toBe(false);
});
});
describe("updatePrefix", () => {
it("updates all provided fields", async () => {
await updatePrefix({
id: 5,
text: "NEW",
color: "#000",
icon: "i",
effect: "e",
active: 0,
});
expect(state.executes[0].sql).toContain("UPDATE custom_prefixes");
expect(state.executes[0].params).toEqual(["NEW", "#000", "i", "e", 0, 5]);
});
it("falls back to active=1 and empty icon/effect", async () => {
await updatePrefix({ id: 5, text: "NEW", color: "#000" });
expect(state.executes[0].params).toEqual(["NEW", "#000", "", "", 1, 5]);
});
});
describe("deletePrefix", () => {
it("deletes by id", async () => {
const res = await deletePrefix({ id: 9 });
expect(res).toEqual({ ok: true, data: {} });
expect(state.executes[0].sql).toContain("DELETE FROM custom_prefixes");
expect(state.executes[0].params).toEqual([9]);
});
});
describe("blacklist words", () => {
it("inserts a trimmed word", async () => {
await addBlacklistWord({ word: " bad " });
expect(state.executes[0].sql).toContain("custom_prefix_blacklist");
expect(state.executes[0].params).toEqual(["bad"]);
});
it("rejects an oversized or empty word", async () => {
expect((await addBlacklistWord({ word: "" })).ok).toBe(false);
expect((await addBlacklistWord({ word: "x".repeat(101) })).ok).toBe(false);
});
it("removes a word by id", async () => {
await removeBlacklistWord({ id: 3 });
expect(state.executes[0].sql).toContain(
"DELETE FROM custom_prefix_blacklist",
);
expect(state.executes[0].params).toEqual([3]);
});
});
describe("updatePrefixSettings", () => {
it("upserts only whitelisted keys", async () => {
const res = await updatePrefixSettings({
settings: { enabled: "1", bogus: "x", max_length: "10" },
});
expect(res).toEqual({ ok: true, data: {} });
expect(state.executes).toHaveLength(2);
const keys = state.executes.map((e) => e.params[0]);
expect(keys).toEqual(["enabled", "max_length"]);
expect(state.executes[0].sql).toContain("custom_prefix_settings");
});
it("does nothing when every key is unknown", async () => {
await updatePrefixSettings({ settings: { nope: "1" } });
expect(state.executes).toHaveLength(0);
});
});
describe("authorization", () => {
it("returns Unauthorized without a context", async () => {
perm.getCtx.mockResolvedValue(null);
expect(await deletePrefix({ id: 1 })).toEqual({
ok: false,
error: "Unauthorized",
});
});
it("denies prefix edits without permission", async () => {
perm.canAccess.mockReturnValue(false);
expect(
await createPrefix({ username: "a", text: "x", color: "y" }),
).toEqual({
ok: false,
error: "Unauthorized",
});
});
});