mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-27 14:31:17 -07:00
Reduce output of config validation errors
This commit is contained in:
@@ -2,12 +2,15 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|||||||
|
|
||||||
import createMockRes from "test-utils/create-mock-res";
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
const { checkAndCopyConfig } = vi.hoisted(() => ({
|
const { checkAndCopyConfig, getSettings } = vi.hoisted(() => ({
|
||||||
checkAndCopyConfig: vi.fn(),
|
checkAndCopyConfig: vi.fn(),
|
||||||
|
getSettings: vi.fn(() => ({})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("utils/config/config", () => ({
|
vi.mock("utils/config/config", () => ({
|
||||||
default: checkAndCopyConfig,
|
default: checkAndCopyConfig,
|
||||||
|
getSettings,
|
||||||
|
CONF_DIR: "/tmp",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import handler from "pages/api/validate";
|
import handler from "pages/api/validate";
|
||||||
@@ -18,13 +21,28 @@ describe("pages/api/validate", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns errors for any configs that don't validate", async () => {
|
it("returns errors for any configs that don't validate", async () => {
|
||||||
checkAndCopyConfig.mockReturnValueOnce(true).mockReturnValueOnce("settings bad").mockReturnValue(true);
|
checkAndCopyConfig
|
||||||
|
.mockReturnValueOnce(true)
|
||||||
|
.mockReturnValueOnce({
|
||||||
|
name: "YAMLException",
|
||||||
|
config: "settings.yaml",
|
||||||
|
reason: "settings bad",
|
||||||
|
mark: { line: 1 },
|
||||||
|
})
|
||||||
|
.mockReturnValue(true);
|
||||||
|
|
||||||
const req = {};
|
const req = {};
|
||||||
const res = createMockRes();
|
const res = createMockRes();
|
||||||
|
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
|
|
||||||
expect(res.body).toEqual(["settings bad"]);
|
expect(res.body).toEqual([
|
||||||
|
{
|
||||||
|
name: "YAMLException",
|
||||||
|
config: "settings.yaml",
|
||||||
|
reason: "settings bad",
|
||||||
|
mark: { line: 1 },
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -324,13 +324,12 @@ describe("pages/index Index routing + SWR branches", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renders config errors when /api/validate returns a list of errors", async () => {
|
it("renders config errors when /api/validate returns a list of errors", async () => {
|
||||||
state.validateData = [{ config: "services.yaml", reason: "broken", mark: { snippet: "x: y" } }];
|
state.validateData = [{ config: "services.yaml", name: "Service 1", reason: "broken", mark: { line: 4 } }];
|
||||||
|
|
||||||
await renderIndex({ initialSettings: { title: "Homepage", layout: {} }, settings: { layout: {} } });
|
await renderIndex({ initialSettings: { title: "Homepage", layout: {} }, settings: { layout: {} } });
|
||||||
|
|
||||||
expect(screen.getByText("services.yaml")).toBeInTheDocument();
|
expect(screen.getByText(/services.yaml/)).toBeInTheDocument();
|
||||||
expect(screen.getByText("broken")).toBeInTheDocument();
|
expect(screen.getByText(/line 4/)).toBeInTheDocument();
|
||||||
expect(screen.getByText("x: y")).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("marks the UI stale when the hash changes and triggers a revalidate reload", async () => {
|
it("marks the UI stale when the hash changes and triggers a revalidate reload", async () => {
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import checkAndCopyConfig from "utils/config/config";
|
import checkAndCopyConfig from "utils/config/config";
|
||||||
|
import createLogger from "utils/logger";
|
||||||
|
|
||||||
const configs = ["docker.yaml", "settings.yaml", "services.yaml", "bookmarks.yaml", "kubernetes.yaml", "proxmox.yaml"];
|
const configs = ["docker.yaml", "settings.yaml", "services.yaml", "bookmarks.yaml", "kubernetes.yaml", "proxmox.yaml"];
|
||||||
|
const logger = createLogger("configValidationHandler");
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const errors = configs.map((config) => checkAndCopyConfig(config)).filter((status) => status !== true);
|
let errors = configs.map((config) => checkAndCopyConfig(config)).filter((status) => status !== true);
|
||||||
|
if (errors.length > 0) {
|
||||||
|
logger.error("Configuration validation errors", errors);
|
||||||
|
errors = errors.map((error) => ({
|
||||||
|
name: error.name,
|
||||||
|
config: error.config,
|
||||||
|
reason: error.reason,
|
||||||
|
mark: { line: error.mark?.line },
|
||||||
|
}));
|
||||||
|
}
|
||||||
res.send(errors);
|
res.send(errors);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -167,11 +167,13 @@ function Index({ initialSettings, fallback }) {
|
|||||||
>
|
>
|
||||||
<div className="bg-amber-200 text-amber-800 dark:text-amber-200 dark:bg-amber-800 p-2 rounded-md font-bold">
|
<div className="bg-amber-200 text-amber-800 dark:text-amber-200 dark:bg-amber-800 p-2 rounded-md font-bold">
|
||||||
<BiError className="float-right w-6 h-6" />
|
<BiError className="float-right w-6 h-6" />
|
||||||
{error.config}
|
{error.name} - {error.config}
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 text-theme-100 dark:text-theme-200">
|
<div className="p-2 text-theme-100 dark:text-theme-200">
|
||||||
<pre className="opacity-50 font-bold pb-2">{error.reason}</pre>
|
<pre className="opacity-50 font-bold pb-2">
|
||||||
<pre className="text-sm">{error.mark.snippet}</pre>
|
Reason: "{error.reason}" at line {error.mark?.line}
|
||||||
|
</pre>
|
||||||
|
<pre className="font-italic">Check logs for details.</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user