fix(updates): enforce a persisted daily check
This commit is contained in:
1 parent
6b74078766
commit
add6ee56ba
8 files changed
+349
-27
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import type { Database } from "@homarr/db";
|
||||
import type { GroupPermissionKey } from "@homarr/definitions";
|
||||
|
||||
import { updateCheckerRouter } from "../update-checker";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getDataAsync: vi.fn(async () => ({
|
||||
data: { availableUpdates: [{ tagName: "v99.0.0" }] },
|
||||
timestamp: new Date(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/request-handler/update-checker", () => ({
|
||||
updateCheckerRequestHandler: {
|
||||
handler: () => ({ getDataAsync: mocks.getDataAsync }),
|
||||
},
|
||||
}));
|
||||
|
||||
const createSession = (permissions: GroupPermissionKey[]): Session => ({
|
||||
user: {
|
||||
id: "user",
|
||||
permissions,
|
||||
colorScheme: "light",
|
||||
},
|
||||
expires: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const createCaller = (session: Session | null) =>
|
||||
updateCheckerRouter.createCaller({
|
||||
db: null as unknown as Database,
|
||||
deviceType: undefined,
|
||||
session,
|
||||
});
|
||||
|
||||
describe("update checker authorization", () => {
|
||||
test("allows administrators to read the cached result", async () => {
|
||||
await expect(createCaller(createSession(["admin"])).getAvailableUpdates()).resolves.toEqual([
|
||||
{ tagName: "v99.0.0" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("rejects authenticated users without admin permission", async () => {
|
||||
await expect(createCaller(createSession([])).getAvailableUpdates()).rejects.toThrow("Permission denied");
|
||||
});
|
||||
|
||||
test("rejects anonymous users", async () => {
|
||||
await expect(createCaller(null).getAvailableUpdates()).rejects.toThrow("UNAUTHORIZED");
|
||||
});
|
||||
|
||||
test("returns an empty result when the cache reader fails", async () => {
|
||||
mocks.getDataAsync.mockRejectedValueOnce(new Error("Redis unavailable"));
|
||||
|
||||
await expect(createCaller(createSession(["admin"])).getAvailableUpdates()).resolves.toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -12,8 +12,8 @@ export const updateCheckerRouter = createTRPCRouter({
|
||||
const data = await handler.getDataAsync();
|
||||
return data.data.availableUpdates;
|
||||
} catch (error) {
|
||||
logger.error(new Error("Failed to get available updates", { cause: error }));
|
||||
return undefined; // We return undefined to not show the indicator in the UI
|
||||
logger.error(new Error("Failed to read the cached update check", { cause: error }));
|
||||
return []; // An empty list hides the indicator without violating TanStack Query's data contract.
|
||||
}
|
||||
}),
|
||||
});
|
||||
Reference in new issue
Block a user