fix: address remaining widget review feedback
This commit is contained in:
1 parent
5553c4e0c5
commit
f00f5b83b5
13 files changed
+149
-26
No files matched your search
@@ -21,7 +21,7 @@ vi.hoisted(() => {
|
||||
|
||||
const createHandler = <T>(integration: { name: string }, data: T) => ({
|
||||
getDataAsync: async () => {
|
||||
if (integration.name === "Offline") throw new Error("offline");
|
||||
if (integration.name === "Offline") throw new Error("offline https://internal.example?token=secret");
|
||||
return { data, timestamp: new Date() };
|
||||
},
|
||||
});
|
||||
@@ -119,9 +119,13 @@ describe("partial integration failures", () => {
|
||||
});
|
||||
expect(calendarResults).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ integration: expect.objectContaining({ id: calendar.offlineId }), error: "offline" }),
|
||||
expect.objectContaining({
|
||||
integration: expect.objectContaining({ id: calendar.offlineId }),
|
||||
error: "Integration request failed",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(JSON.stringify(calendarResults)).not.toContain("secret");
|
||||
|
||||
const mediaServer = await setupAsync("plex");
|
||||
const streamResults = await mediaServerRouter
|
||||
@@ -129,9 +133,14 @@ describe("partial integration failures", () => {
|
||||
.getCurrentStreams({ integrationIds: mediaServer.integrationIds, showOnlyPlaying: false });
|
||||
expect(streamResults).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ integrationId: mediaServer.offlineId, sessions: [], error: "offline" }),
|
||||
expect.objectContaining({
|
||||
integrationId: mediaServer.offlineId,
|
||||
sessions: [],
|
||||
error: "Integration request failed",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(JSON.stringify(streamResults)).not.toContain("secret");
|
||||
});
|
||||
|
||||
test("media request responses expose failed integrations without dropping successful data", async () => {
|
||||
@@ -156,13 +165,15 @@ describe("partial integration failures", () => {
|
||||
}),
|
||||
]);
|
||||
expect(latest.failedIntegrations).toEqual([
|
||||
expect.objectContaining({ integrationId: mediaRequests.offlineId, error: "offline" }),
|
||||
expect.objectContaining({ integrationId: mediaRequests.offlineId, error: "Integration request failed" }),
|
||||
]);
|
||||
expect(JSON.stringify(latest)).not.toContain("secret");
|
||||
|
||||
const stats = await caller.getStats({ integrationIds: mediaRequests.integrationIds });
|
||||
expect(stats.failedIntegrations).toEqual([
|
||||
expect.objectContaining({ integrationId: mediaRequests.offlineId, error: "offline" }),
|
||||
expect.objectContaining({ integrationId: mediaRequests.offlineId, error: "Integration request failed" }),
|
||||
]);
|
||||
expect(JSON.stringify(stats)).not.toContain("secret");
|
||||
expect(stats.stats).toHaveLength(1);
|
||||
expect(stats.users).toEqual([
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { stringify } from "superjson";
|
||||
// @vitest-environment node
|
||||
|
||||
import { parse, stringify } from "superjson";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
@@ -8,6 +10,8 @@ import { eq } from "@homarr/db";
|
||||
import { boards, items, users } from "@homarr/db/schema";
|
||||
import { createDb } from "@homarr/db/test";
|
||||
|
||||
import { boardRouter } from "../../board";
|
||||
import { optionsRouter } from "../../widgets/options";
|
||||
import { timetableRouter } from "../../widgets/timetable";
|
||||
|
||||
const mocks = vi.hoisted(() => ({ getTimetable: vi.fn(), searchStations: vi.fn(), lookup: vi.fn() }));
|
||||
@@ -123,7 +127,7 @@ describe("timetableRouter source authorization", () => {
|
||||
"http://timetable.internal:8080",
|
||||
"http://[::1]:8080",
|
||||
"http://[::ffff:127.0.0.1]:8080",
|
||||
"http://[fd00::1]:8080",
|
||||
"https://[fd00::1]:8443",
|
||||
"http://[64:ff9b::7f00:1]:8080",
|
||||
"http://[2002:7f00:1::]:8080",
|
||||
])("blocks an unsaved private endpoint %s even for board modifiers", async (baseUrl) => {
|
||||
@@ -182,6 +186,66 @@ describe("timetableRouter source authorization", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("blocks changing a saved timetable to a private endpoint through item options", async () => {
|
||||
const db = createDb();
|
||||
const { boardId, itemId, ownerId } = await createTimetableItemAsync(db, {
|
||||
baseUrl: "https://search.ch",
|
||||
isPublic: false,
|
||||
});
|
||||
const caller = optionsRouter.createCaller({ db, deviceType: undefined, session: createSession(ownerId) });
|
||||
|
||||
await expect(
|
||||
caller.saveItemOptions({ boardId, itemId, newOptions: { baseUrl: "http://127.0.0.1:8080" } }),
|
||||
).rejects.toMatchObject({ code: "BAD_REQUEST" });
|
||||
|
||||
const item = await db.query.items.findFirst({ where: eq(items.id, itemId) });
|
||||
expect(parse<Record<string, unknown>>(item?.options ?? "{}")).toMatchObject({ baseUrl: "https://search.ch" });
|
||||
});
|
||||
|
||||
it("blocks changing a saved timetable to a private endpoint through a board save", async () => {
|
||||
const db = createDb();
|
||||
const { boardId, itemId, ownerId } = await createTimetableItemAsync(db, {
|
||||
baseUrl: "https://search.ch",
|
||||
isPublic: false,
|
||||
});
|
||||
const caller = boardRouter.createCaller({ db, deviceType: undefined, session: createSession(ownerId) });
|
||||
|
||||
await expect(
|
||||
caller.saveBoard({
|
||||
id: boardId,
|
||||
sections: [],
|
||||
items: [
|
||||
{
|
||||
id: itemId,
|
||||
kind: "timetable",
|
||||
options: { baseUrl: "http://127.0.0.1:8080" },
|
||||
integrationIds: [],
|
||||
layouts: [],
|
||||
advancedOptions: { title: null, customCssClasses: [], borderColor: "" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "BAD_REQUEST" });
|
||||
});
|
||||
|
||||
it("blocks adding a timetable with a private endpoint", async () => {
|
||||
const db = createDb();
|
||||
const { boardId, ownerId } = await createTimetableItemAsync(db, {
|
||||
baseUrl: "https://search.ch",
|
||||
isPublic: false,
|
||||
});
|
||||
const caller = boardRouter.createCaller({ db, deviceType: undefined, session: createSession(ownerId) });
|
||||
|
||||
await expect(
|
||||
caller.addItem({
|
||||
boardId,
|
||||
kind: "timetable",
|
||||
options: { baseUrl: "http://127.0.0.1:8080" },
|
||||
integrationIds: [],
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "BAD_REQUEST" });
|
||||
});
|
||||
|
||||
it("rejects an item ID that does not belong to the requested board", async () => {
|
||||
const db = createDb();
|
||||
const first = await createTimetableItemAsync(db, { baseUrl: "http://timetable.internal:8080", isPublic: false });
|
||||
|
||||
Reference in new issue
Block a user