Files
Checkmate/server/tests/services/system/SettingsService.test.js
T
2025-12-16 21:17:29 +00:00

151 lines
5.3 KiB
JavaScript

import SettingsService from "@/services/system/SettingsService.js";
import ApiError from "@/utils/ApiError.js";
// Mock models and mongoose db
jest.mock("@/db/models/index.js", () => ({
SystemSettings: {
findById: jest.fn(),
create: jest.fn(),
findOneAndUpdate: jest.fn(),
},
}));
import { SystemSettings } from "@/db/models/index.js";
jest.mock("mongoose", () => ({
connection: { db: null },
}));
import mongoose from "mongoose";
// Mock EmailService used via setEmailService
class MockEmailService {
rebuildTransport = jest.fn();
}
describe("SettingsService", () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe("get", () => {
it("creates default settings when missing and returns settings", async () => {
const select = (v) => ({ select: jest.fn().mockResolvedValue(v) });
SystemSettings.findById
.mockReturnValueOnce(select(null)) // initial check
.mockReturnValueOnce(select({ _id: "global", systemEmailHost: "" })); // after create
SystemSettings.create.mockResolvedValue({ _id: "global" });
const svc = new SettingsService();
const res = await svc.get();
expect(SystemSettings.create).toHaveBeenCalledWith({ _id: "global" });
expect(res).toEqual({ _id: "global", systemEmailHost: "" });
});
it("throws when settings cannot be loaded after create", async () => {
const select = (v) => ({ select: jest.fn().mockResolvedValue(v) });
SystemSettings.findById
.mockReturnValueOnce(select(null))
.mockReturnValueOnce(select(null));
SystemSettings.create.mockResolvedValue({ _id: "global" });
const svc = new SettingsService();
await expect(svc.get()).rejects.toMatchObject({
message: "Unable to load system settings",
status: 500,
});
});
});
describe("updateEmailSettings", () => {
it("sets provided fields, unsets others, rebuilds transport and returns settings", async () => {
const incoming = {
systemEmailHost: "smtp.example.com",
systemEmailPort: 587,
systemEmailSecure: false,
};
const updated = { _id: "global", ...incoming };
SystemSettings.findOneAndUpdate.mockResolvedValue(updated);
const svc = new SettingsService();
const email = new MockEmailService();
svc.setEmailService(email);
const res = await svc.updateEmailSettings(incoming);
expect(SystemSettings.findOneAndUpdate).toHaveBeenCalledWith(
{ _id: "global" },
expect.objectContaining({ $set: expect.any(Object), $unset: expect.any(Object) }),
expect.objectContaining({ new: true, upsert: true, setDefaultsOnInsert: true })
);
expect(email.rebuildTransport).toHaveBeenCalledWith(updated);
expect(res).toBe(updated);
});
it("throws when update returns no settings", async () => {
SystemSettings.findOneAndUpdate.mockResolvedValue(null);
const svc = new SettingsService();
const email = new MockEmailService();
svc.setEmailService(email);
await expect(svc.updateEmailSettings({})).rejects.toMatchObject({
message: "Unable to update system settings",
status: 500,
});
});
});
describe("updateRetentionPolicy", () => {
it("updates TTL via db.command and verifies expireAfterSeconds", async () => {
SystemSettings.findOneAndUpdate.mockResolvedValue({ _id: "global" });
const mockDb = {
command: jest
.fn()
// collMod
.mockResolvedValueOnce({ ok: 1 })
// listCollections
.mockResolvedValueOnce({ cursor: { firstBatch: [{ options: { expireAfterSeconds: 86400 } }] }, ok: 1 }),
};
mongoose.connection.db = mockDb;
const svc = new SettingsService();
const result = await svc.updateRetentionPolicy(1); // 1 day
expect(mockDb.command).toHaveBeenCalledWith({ collMod: "checks", expireAfterSeconds: 86400 });
expect(result).toBe(1);
});
it("throws when db not initialized", async () => {
SystemSettings.findOneAndUpdate.mockResolvedValue({ _id: "global" });
mongoose.connection.db = null;
const svc = new SettingsService();
await expect(svc.updateRetentionPolicy(1)).rejects.toMatchObject({
message: "Database not initialized",
status: 500,
});
});
it("throws when collMod fails", async () => {
SystemSettings.findOneAndUpdate.mockResolvedValue({ _id: "global" });
const mockDb = { command: jest.fn().mockResolvedValueOnce({ ok: 0 }) };
mongoose.connection.db = mockDb;
const svc = new SettingsService();
await expect(svc.updateRetentionPolicy(1)).rejects.toMatchObject({
message: "Failed to apply TTL to checks collection",
status: 500,
});
});
it("throws when TTL verification fails", async () => {
SystemSettings.findOneAndUpdate.mockResolvedValue({ _id: "global" });
const mockDb = {
command: jest
.fn()
.mockResolvedValueOnce({ ok: 1 })
.mockResolvedValueOnce({ cursor: { firstBatch: [{ options: { expireAfterSeconds: 30 } }] }, ok: 1 }),
};
mongoose.connection.db = mockDb;
const svc = new SettingsService();
await expect(svc.updateRetentionPolicy(1)).rejects.toMatchObject({
message: "TTL verification failed: expireAfterSeconds not applied",
status: 500,
});
});
});
});