mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-08 00:40:52 +08:00
test: cover config source modules
This commit is contained in:
@@ -5,6 +5,14 @@ import yaml from "js-yaml";
|
|||||||
|
|
||||||
import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config";
|
import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config";
|
||||||
|
|
||||||
|
export function getDefaultDockerArgs(platform = process.platform) {
|
||||||
|
if (platform !== "win32" && platform !== "darwin") {
|
||||||
|
return { socketPath: "/var/run/docker.sock" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { host: "127.0.0.1" };
|
||||||
|
}
|
||||||
|
|
||||||
export default function getDockerArguments(server) {
|
export default function getDockerArguments(server) {
|
||||||
checkAndCopyConfig("docker.yaml");
|
checkAndCopyConfig("docker.yaml");
|
||||||
|
|
||||||
@@ -14,11 +22,7 @@ export default function getDockerArguments(server) {
|
|||||||
const servers = yaml.load(configData);
|
const servers = yaml.load(configData);
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
if (process.platform !== "win32" && process.platform !== "darwin") {
|
return getDefaultDockerArgs();
|
||||||
return { socketPath: "/var/run/docker.sock" };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { host: "127.0.0.1" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (servers[server]) {
|
if (servers[server]) {
|
||||||
|
|||||||
95
src/utils/config/docker.test.js
Normal file
95
src/utils/config/docker.test.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { fs, yaml, config, checkAndCopyConfig } = vi.hoisted(() => ({
|
||||||
|
fs: {
|
||||||
|
readFileSync: vi.fn((filePath, encoding) => {
|
||||||
|
if (String(filePath).endsWith("/docker.yaml") && encoding === "utf8") return "docker-yaml";
|
||||||
|
return Buffer.from(String(filePath));
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
yaml: {
|
||||||
|
load: vi.fn(),
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
CONF_DIR: "/conf",
|
||||||
|
substituteEnvironmentVars: vi.fn((s) => s),
|
||||||
|
},
|
||||||
|
checkAndCopyConfig: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("fs", () => ({
|
||||||
|
readFileSync: fs.readFileSync,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("js-yaml", () => ({
|
||||||
|
default: yaml,
|
||||||
|
...yaml,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/config/config", () => ({
|
||||||
|
default: checkAndCopyConfig,
|
||||||
|
...config,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import getDockerArguments, { getDefaultDockerArgs } from "./docker";
|
||||||
|
|
||||||
|
describe("utils/config/docker", () => {
|
||||||
|
it("getDefaultDockerArgs returns a socketPath on linux and host on darwin", () => {
|
||||||
|
expect(getDefaultDockerArgs("linux")).toEqual({ socketPath: "/var/run/docker.sock" });
|
||||||
|
expect(getDefaultDockerArgs("darwin")).toEqual({ host: "127.0.0.1" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns default args when no server is given", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({});
|
||||||
|
|
||||||
|
const args = getDockerArguments();
|
||||||
|
|
||||||
|
expect(checkAndCopyConfig).toHaveBeenCalledWith("docker.yaml");
|
||||||
|
expect(args).toEqual(expect.objectContaining({ host: expect.any(String) }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns socket config when server has a socket", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({
|
||||||
|
"docker-local": { socket: "/tmp/docker.sock", swarm: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = getDockerArguments("docker-local");
|
||||||
|
|
||||||
|
expect(args).toEqual({ conn: { socketPath: "/tmp/docker.sock" }, swarm: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns host/port/tls/protocol/headers config when provided", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({
|
||||||
|
remote: {
|
||||||
|
host: "10.0.0.1",
|
||||||
|
port: 2376,
|
||||||
|
swarm: false,
|
||||||
|
protocol: "http",
|
||||||
|
headers: { "X-Test": "1" },
|
||||||
|
tls: { caFile: "ca.pem", certFile: "cert.pem", keyFile: "key.pem" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = getDockerArguments("remote");
|
||||||
|
|
||||||
|
expect(args).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
swarm: false,
|
||||||
|
conn: expect.objectContaining({
|
||||||
|
host: "10.0.0.1",
|
||||||
|
port: 2376,
|
||||||
|
protocol: "http",
|
||||||
|
headers: { "X-Test": "1" },
|
||||||
|
ca: expect.any(Buffer),
|
||||||
|
cert: expect.any(Buffer),
|
||||||
|
key: expect.any(Buffer),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null when server is not configured", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({ other: { host: "x" } });
|
||||||
|
expect(getDockerArguments("missing")).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
108
src/utils/config/kubernetes.test.js
Normal file
108
src/utils/config/kubernetes.test.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { fs, yaml, config, checkAndCopyConfig, kube, apiExt } = vi.hoisted(() => {
|
||||||
|
const apiExt = {
|
||||||
|
readCustomResourceDefinitionStatus: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const kube = {
|
||||||
|
loadFromCluster: vi.fn(),
|
||||||
|
loadFromDefault: vi.fn(),
|
||||||
|
makeApiClient: vi.fn(() => apiExt),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
fs: {
|
||||||
|
readFileSync: vi.fn(() => "kube-yaml"),
|
||||||
|
},
|
||||||
|
yaml: {
|
||||||
|
load: vi.fn(),
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
CONF_DIR: "/conf",
|
||||||
|
substituteEnvironmentVars: vi.fn((s) => s),
|
||||||
|
},
|
||||||
|
checkAndCopyConfig: vi.fn(),
|
||||||
|
kube,
|
||||||
|
apiExt,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock("fs", () => ({
|
||||||
|
readFileSync: fs.readFileSync,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("js-yaml", () => ({
|
||||||
|
default: yaml,
|
||||||
|
...yaml,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/config/config", () => ({
|
||||||
|
default: checkAndCopyConfig,
|
||||||
|
...config,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@kubernetes/client-node", () => ({
|
||||||
|
ApiextensionsV1Api: class ApiextensionsV1Api {},
|
||||||
|
KubeConfig: class KubeConfig {
|
||||||
|
loadFromCluster() {
|
||||||
|
return kube.loadFromCluster();
|
||||||
|
}
|
||||||
|
loadFromDefault() {
|
||||||
|
return kube.loadFromDefault();
|
||||||
|
}
|
||||||
|
makeApiClient() {
|
||||||
|
return kube.makeApiClient();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { checkCRD, getKubeConfig, getKubernetes } from "./kubernetes";
|
||||||
|
|
||||||
|
describe("utils/config/kubernetes", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getKubernetes loads and parses kubernetes.yaml", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({ mode: "disabled" });
|
||||||
|
|
||||||
|
expect(getKubernetes()).toEqual({ mode: "disabled" });
|
||||||
|
expect(checkAndCopyConfig).toHaveBeenCalledWith("kubernetes.yaml");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getKubeConfig returns null when disabled", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({ mode: "disabled" });
|
||||||
|
expect(getKubeConfig()).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getKubeConfig loads from cluster/default based on mode", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({ mode: "cluster" });
|
||||||
|
const kc1 = getKubeConfig();
|
||||||
|
expect(kube.loadFromCluster).toHaveBeenCalled();
|
||||||
|
expect(kc1).not.toBeNull();
|
||||||
|
|
||||||
|
yaml.load.mockReturnValueOnce({ mode: "default" });
|
||||||
|
const kc2 = getKubeConfig();
|
||||||
|
expect(kube.loadFromDefault).toHaveBeenCalled();
|
||||||
|
expect(kc2).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checkCRD returns true when the CRD exists", async () => {
|
||||||
|
apiExt.readCustomResourceDefinitionStatus.mockResolvedValueOnce({ ok: true });
|
||||||
|
const logger = { error: vi.fn() };
|
||||||
|
|
||||||
|
await expect(checkCRD("x.example", kube, logger)).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checkCRD returns false and logs on 403", async () => {
|
||||||
|
apiExt.readCustomResourceDefinitionStatus.mockRejectedValueOnce({
|
||||||
|
statusCode: 403,
|
||||||
|
body: { message: "nope" },
|
||||||
|
});
|
||||||
|
const logger = { error: vi.fn() };
|
||||||
|
|
||||||
|
await expect(checkCRD("x.example", kube, logger)).resolves.toBe(false);
|
||||||
|
expect(logger.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
41
src/utils/config/proxmox.test.js
Normal file
41
src/utils/config/proxmox.test.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { fs, yaml, config, checkAndCopyConfig } = vi.hoisted(() => ({
|
||||||
|
fs: {
|
||||||
|
readFileSync: vi.fn(() => "proxmox-yaml"),
|
||||||
|
},
|
||||||
|
yaml: {
|
||||||
|
load: vi.fn(),
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
CONF_DIR: "/conf",
|
||||||
|
substituteEnvironmentVars: vi.fn((s) => s),
|
||||||
|
},
|
||||||
|
checkAndCopyConfig: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("fs", () => ({
|
||||||
|
readFileSync: fs.readFileSync,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("js-yaml", () => ({
|
||||||
|
default: yaml,
|
||||||
|
...yaml,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/config/config", () => ({
|
||||||
|
default: checkAndCopyConfig,
|
||||||
|
...config,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { getProxmoxConfig } from "./proxmox";
|
||||||
|
|
||||||
|
describe("utils/config/proxmox", () => {
|
||||||
|
it("loads and parses proxmox.yaml", () => {
|
||||||
|
yaml.load.mockReturnValueOnce({ pve: { url: "http://pve" } });
|
||||||
|
|
||||||
|
expect(getProxmoxConfig()).toEqual({ pve: { url: "http://pve" } });
|
||||||
|
expect(checkAndCopyConfig).toHaveBeenCalledWith("proxmox.yaml");
|
||||||
|
expect(fs.readFileSync).toHaveBeenCalledWith("/conf/proxmox.yaml", "utf8");
|
||||||
|
});
|
||||||
|
});
|
||||||
30
src/utils/config/shvl.test.js
Normal file
30
src/utils/config/shvl.test.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { get, set } from "./shvl";
|
||||||
|
|
||||||
|
describe("utils/config/shvl", () => {
|
||||||
|
it("get reads nested paths with arrays and returns default when missing", () => {
|
||||||
|
const obj = { a: { b: [{ c: 1 }] } };
|
||||||
|
|
||||||
|
expect(get(obj, "a.b[0].c")).toBe(1);
|
||||||
|
expect(get(obj, "a.b[1].c", "dflt")).toBe("dflt");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("set creates nested objects/arrays as needed", () => {
|
||||||
|
const obj = {};
|
||||||
|
set(obj, "a.b[0].c", 123);
|
||||||
|
|
||||||
|
expect(obj).toEqual({ a: { b: [{ c: 123 }] } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("set blocks prototype pollution", () => {
|
||||||
|
const obj = {};
|
||||||
|
set(obj, "__proto__.polluted", true);
|
||||||
|
set(obj, "a.__proto__.polluted", true);
|
||||||
|
set(obj, "constructor.prototype.polluted", true);
|
||||||
|
|
||||||
|
expect(obj.polluted).toBeUndefined();
|
||||||
|
expect({}.polluted).toBeUndefined();
|
||||||
|
expect(Object.prototype.polluted).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user