Files
AuroraBot-discord/web/src/server.settings.test.ts

171 lines
5.2 KiB
TypeScript

import { describe, expect, it, mock, beforeEach, afterEach, jest } from "bun:test";
import { type WebServerInstance } from "./server";
// Mock the dependencies
const mockConfig = {
leveling: {
base: 100,
exponent: 1.5,
chat: { minXp: 10, maxXp: 20, cooldownMs: 60000 }
},
economy: {
daily: { amount: 100n, streakBonus: 10n, weeklyBonus: 50n, cooldownMs: 86400000 },
transfers: { allowSelfTransfer: false, minAmount: 50n },
exam: { multMin: 1.5, multMax: 2.5 }
},
inventory: { maxStackSize: 99n, maxSlots: 20 },
lootdrop: {
spawnChance: 0.1,
cooldownMs: 3600000,
minMessages: 10,
reward: { min: 100, max: 500, currency: "gold" }
},
commands: { "help": true },
system: {},
moderation: {
prune: { maxAmount: 100, confirmThreshold: 50, batchSize: 100, batchDelayMs: 1000 },
cases: { dmOnWarn: true }
}
};
const mockSaveConfig = jest.fn();
// Mock @shared/lib/config using mock.module
mock.module("@shared/lib/config", () => ({
config: mockConfig,
saveConfig: mockSaveConfig,
GameConfigType: {}
}));
// Mock BotClient
const mockGuild = {
roles: {
cache: [
{ id: "role1", name: "Admin", hexColor: "#ffffff", position: 1 },
{ id: "role2", name: "User", hexColor: "#000000", position: 0 }
]
},
channels: {
cache: [
{ id: "chan1", name: "general", type: 0 }
]
}
};
mock.module("../../bot/lib/BotClient", () => ({
AuroraClient: {
guilds: {
cache: {
get: () => mockGuild
}
},
commands: [
{ data: { name: "ping" } }
],
knownCommands: new Map([
["ping", "utility"],
["help", "utility"],
["disabled-cmd", "admin"]
])
}
}));
mock.module("@shared/lib/env", () => ({
env: {
DISCORD_GUILD_ID: "123456789"
}
}));
// Mock spawn
mock.module("bun", () => {
return {
spawn: jest.fn(() => ({
unref: () => { }
})),
serve: Bun.serve
};
});
// Import createWebServer after mocks
import { createWebServer } from "./server";
describe("Settings API", () => {
let serverInstance: WebServerInstance;
const PORT = 3009;
const BASE_URL = `http://localhost:${PORT}`;
beforeEach(async () => {
jest.clearAllMocks();
serverInstance = await createWebServer({ port: PORT });
});
afterEach(async () => {
if (serverInstance) {
await serverInstance.stop();
}
});
it("GET /api/settings should return current configuration", async () => {
const res = await fetch(`${BASE_URL}/api/settings`);
expect(res.status).toBe(200);
const data = await res.json();
// Check if BigInts are converted to strings
expect(data.economy.daily.amount).toBe("100");
expect(data.leveling.base).toBe(100);
});
it("POST /api/settings should save valid configuration via merge", async () => {
// We only send a partial update, expecting the server to merge it
// Note: For now the server implementation might still default to overwrite if we haven't updated it yet.
// But the user requested "partial vs full" fix.
// Let's assume we implement the merge logic.
const partialConfig = { studentRole: "new-role-partial" };
const res = await fetch(`${BASE_URL}/api/settings`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(partialConfig)
});
expect(res.status).toBe(200);
// Expect saveConfig to be called with the MERGED result
expect(mockSaveConfig).toHaveBeenCalledWith(expect.objectContaining({
studentRole: "new-role-partial",
leveling: mockConfig.leveling // Should keep existing values
}));
});
it("POST /api/settings should return 400 when save fails", async () => {
mockSaveConfig.mockImplementationOnce(() => {
throw new Error("Validation failed");
});
const res = await fetch(`${BASE_URL}/api/settings`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}) // Empty might be valid partial, but mocks throw
});
expect(res.status).toBe(400);
const data = await res.json();
expect(data.details).toBe("Validation failed");
});
it("GET /api/settings/meta should return simplified metadata", async () => {
const res = await fetch(`${BASE_URL}/api/settings/meta`);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.roles).toHaveLength(2);
expect(data.roles[0]).toEqual({ id: "role1", name: "Admin", color: "#ffffff" });
expect(data.channels[0]).toEqual({ id: "chan1", name: "general", type: 0 });
// Check new commands structure
expect(data.commands).toBeArray();
expect(data.commands.length).toBeGreaterThan(0);
expect(data.commands[0]).toHaveProperty("name");
expect(data.commands[0]).toHaveProperty("category");
});
});