mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 15:45:23 +00:00
feat: host lock api so a plugin can load hosts from a config file (#1581)
This commit is contained in:
1 parent
1cdbfc674c
commit
2c785fe35b
34 files changed
+1388
-52
No files matched your search
@@ -0,0 +1,113 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user