feat(FN-2952): validate unavailableNodePolicy in PUT /settings

Adds server-side validation for the `unavailableNodePolicy` field on the
PUT /api/settings route. Accepts only `block` or `fallback-local`;
rejects unknown strings and non-string values with 400.

- packages/dashboard/src/routes/register-settings-memory-routes.ts:
  call `validateUnavailableNodePolicy` and throw `badRequest` on invalid input
- packages/dashboard/src/__tests__/routes.test.ts: cover accept,
  invalid-value, and non-string cases

Fusion-Task-Id: FN-2952
This commit is contained in:
Fusion
2026-04-29 14:51:38 -07:00
committed by gsxdsm
parent 7e832bbf92
commit 7afbd553c2
2 changed files with 55 additions and 0 deletions

View File

@@ -14401,6 +14401,53 @@ describe("PUT /settings", () => {
expect(store.updateSettings).not.toHaveBeenCalled();
});
it("accepts unavailableNodePolicy fallback-local", async () => {
const updatedSettings = {
...DEFAULT_SETTINGS,
unavailableNodePolicy: "fallback-local",
};
(store.updateSettings as ReturnType<typeof vi.fn>).mockResolvedValue(updatedSettings);
const res = await REQUEST(
buildApp(),
"PUT",
"/api/settings",
JSON.stringify({ unavailableNodePolicy: "fallback-local" }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(200);
expect(store.updateSettings).toHaveBeenCalledWith({ unavailableNodePolicy: "fallback-local" });
});
it("rejects invalid unavailableNodePolicy values", async () => {
const res = await REQUEST(
buildApp(),
"PUT",
"/api/settings",
JSON.stringify({ unavailableNodePolicy: "auto-retry" }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(400);
expect(res.body.error).toContain("unavailableNodePolicy");
expect(store.updateSettings).not.toHaveBeenCalled();
});
it("rejects non-string unavailableNodePolicy values", async () => {
const res = await REQUEST(
buildApp(),
"PUT",
"/api/settings",
JSON.stringify({ unavailableNodePolicy: 42 }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(400);
expect(res.body.error).toContain("unavailableNodePolicy");
expect(store.updateSettings).not.toHaveBeenCalled();
});
it("returns 500 on store update error", async () => {
(store.updateSettings as ReturnType<typeof vi.fn>).mockRejectedValue(new Error("Write failed"));