mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
114 lines
3.3 KiB
TypeScript
114 lines
3.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../utils/logger.js", () => ({
|
|
pluginLogger: { warn: vi.fn() },
|
|
}));
|
|
|
|
const {
|
|
assertHostsUnlocked,
|
|
assertManualCreateAllowed,
|
|
clearHostLocks,
|
|
hasHostLocks,
|
|
hostLock,
|
|
HostLockedError,
|
|
manualCreateBlockedBy,
|
|
provideHostLocks,
|
|
requireManualCreate,
|
|
requireUnlockedHost,
|
|
} = await import("../../hosts/locks.js");
|
|
|
|
const provider = (locked: number[], allowManualCreate = true) => ({
|
|
isLocked: (hostId: number) => locked.includes(hostId),
|
|
note: (hostId: number) => `id ${hostId}`,
|
|
allowManualCreate: () => allowManualCreate,
|
|
});
|
|
|
|
function fakeRes() {
|
|
const res = {
|
|
code: 0,
|
|
body: null as unknown,
|
|
status(code: number) {
|
|
res.code = code;
|
|
return { json: (body: unknown) => (res.body = body) };
|
|
},
|
|
};
|
|
return res;
|
|
}
|
|
|
|
afterEach(() => clearHostLocks());
|
|
|
|
describe("host locks", () => {
|
|
it("reports nothing without providers", () => {
|
|
expect(hasHostLocks()).toBe(false);
|
|
expect(hostLock(1)).toBeNull();
|
|
expect(() => assertHostsUnlocked([1, 2])).not.toThrow();
|
|
expect(manualCreateBlockedBy()).toBeNull();
|
|
});
|
|
|
|
it("names the plugin and note for a locked host", () => {
|
|
provideHostLocks("cfg", "Config", provider([3]));
|
|
expect(hostLock(3)).toEqual({
|
|
pluginId: "cfg",
|
|
pluginName: "Config",
|
|
note: "id 3",
|
|
});
|
|
expect(hostLock(4)).toBeNull();
|
|
expect(() => assertHostsUnlocked([4, 3])).toThrow(HostLockedError);
|
|
});
|
|
|
|
it("lets the locking plugin through", () => {
|
|
provideHostLocks("cfg", "Config", provider([3], false));
|
|
expect(() => assertHostsUnlocked([3], "cfg")).not.toThrow();
|
|
expect(() => assertManualCreateAllowed("cfg")).not.toThrow();
|
|
expect(() => assertManualCreateAllowed()).toThrow(/managed by Config/);
|
|
});
|
|
|
|
it("drops a provider when removed", () => {
|
|
const remove = provideHostLocks("cfg", "Config", provider([3], false));
|
|
remove();
|
|
expect(hostLock(3)).toBeNull();
|
|
expect(manualCreateBlockedBy()).toBeNull();
|
|
});
|
|
|
|
it("never locks when a provider throws", () => {
|
|
provideHostLocks("cfg", "Config", {
|
|
isLocked: () => {
|
|
throw new Error("broken");
|
|
},
|
|
note: () => null,
|
|
allowManualCreate: () => {
|
|
throw new Error("broken");
|
|
},
|
|
});
|
|
expect(hostLock(1)).toBeNull();
|
|
expect(manualCreateBlockedBy()).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("lock route guards", () => {
|
|
it("answers 403 HOST_LOCKED for a locked :id", () => {
|
|
provideHostLocks("cfg", "Config", provider([7]));
|
|
const res = fakeRes();
|
|
const next = vi.fn();
|
|
requireUnlockedHost({ params: { id: "7" } }, res, next);
|
|
expect(next).not.toHaveBeenCalled();
|
|
expect(res.code).toBe(403);
|
|
expect(res.body).toMatchObject({ code: "HOST_LOCKED", lockedBy: "cfg" });
|
|
|
|
requireUnlockedHost({ params: { id: "8" } }, fakeRes(), next);
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("blocks manual creates only while a provider says so", () => {
|
|
const next = vi.fn();
|
|
requireManualCreate({}, fakeRes(), next);
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
|
|
provideHostLocks("cfg", "Config", provider([], false));
|
|
const res = fakeRes();
|
|
requireManualCreate({}, res, next);
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.code).toBe(403);
|
|
});
|
|
});
|