fix: address Copilot cache review
This commit is contained in:
1 parent
cd6c83a7b0
commit
388a74086e
4 files changed
+46
-2
No files matched your search
@@ -0,0 +1,21 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
vi.mock("./connection", () => ({
|
||||
createRedisConnection: () => null,
|
||||
}));
|
||||
|
||||
import { createLockChannel } from "./channel";
|
||||
|
||||
describe("createLockChannel", () => {
|
||||
test("degrades gracefully when Redis connections are disabled", async () => {
|
||||
const lock = createLockChannel("test-lock");
|
||||
|
||||
const token = await lock.acquireAsync(60);
|
||||
|
||||
expect(token).toEqual(expect.any(String));
|
||||
if (!token) throw new Error("Expected the fallback lock to return a token");
|
||||
await expect(lock.releaseAsync(token)).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -139,11 +139,17 @@ export const createLockChannel = (name: string) => {
|
||||
return {
|
||||
acquireAsync: async (ttlSeconds: number) => {
|
||||
const token = createId();
|
||||
const result = await getSetClient.set(name, token, "EX", ttlSeconds, "NX");
|
||||
const client = getSetClient as typeof getSetClient | null;
|
||||
if (!client) return token;
|
||||
|
||||
const result = await client.set(name, token, "EX", ttlSeconds, "NX");
|
||||
return result === "OK" ? token : null;
|
||||
},
|
||||
releaseAsync: async (token: string) => {
|
||||
await getSetClient.eval(
|
||||
const client = getSetClient as typeof getSetClient | null;
|
||||
if (!client) return;
|
||||
|
||||
await client.eval(
|
||||
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end",
|
||||
1,
|
||||
name,
|
||||
|
||||
@@ -11,10 +11,12 @@ const mocks = vi.hoisted(() => {
|
||||
const values = new Map<string, StoredValue>();
|
||||
const locks = new Set<string>();
|
||||
const listReleases = vi.fn();
|
||||
const redisOperation = vi.fn();
|
||||
|
||||
return {
|
||||
env: { NO_EXTERNAL_CONNECTION: false },
|
||||
listReleases,
|
||||
redisOperation,
|
||||
values,
|
||||
locks,
|
||||
removeFreshResult() {
|
||||
@@ -28,6 +30,7 @@ vi.mock("@homarr/common/env", () => ({ env: mocks.env }));
|
||||
vi.mock("@homarr/redis", () => ({
|
||||
createGetSetChannel: (name: string) => ({
|
||||
getAsync: async () => {
|
||||
mocks.redisOperation();
|
||||
const stored = mocks.values.get(name);
|
||||
if (!stored) return null;
|
||||
if (stored.expiresAt !== null && Date.now() >= stored.expiresAt) {
|
||||
@@ -37,22 +40,26 @@ vi.mock("@homarr/redis", () => ({
|
||||
return stored.value;
|
||||
},
|
||||
setAsync: async (value: unknown, options?: { ttlSeconds?: number }) => {
|
||||
mocks.redisOperation();
|
||||
mocks.values.set(name, {
|
||||
value,
|
||||
expiresAt: options?.ttlSeconds ? Date.now() + options.ttlSeconds * 1_000 : null,
|
||||
});
|
||||
},
|
||||
removeAsync: async () => {
|
||||
mocks.redisOperation();
|
||||
mocks.values.delete(name);
|
||||
},
|
||||
}),
|
||||
createLockChannel: (name: string) => ({
|
||||
acquireAsync: async () => {
|
||||
mocks.redisOperation();
|
||||
if (mocks.locks.has(name)) return null;
|
||||
mocks.locks.add(name);
|
||||
return "test-lock-token";
|
||||
},
|
||||
releaseAsync: async () => {
|
||||
mocks.redisOperation();
|
||||
mocks.locks.delete(name);
|
||||
},
|
||||
}),
|
||||
@@ -94,6 +101,7 @@ describe("persisted update checker cache", () => {
|
||||
vi.restoreAllMocks();
|
||||
mocks.env.NO_EXTERNAL_CONNECTION = false;
|
||||
mocks.listReleases.mockReset();
|
||||
mocks.redisOperation.mockReset();
|
||||
mocks.values.clear();
|
||||
mocks.locks.clear();
|
||||
});
|
||||
@@ -202,5 +210,6 @@ describe("persisted update checker cache", () => {
|
||||
|
||||
await expect(handler.getDataAsync()).resolves.toMatchObject({ data: { availableUpdates: [] } });
|
||||
expect(mocks.listReleases).not.toHaveBeenCalled();
|
||||
expect(mocks.redisOperation).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -42,6 +42,14 @@ const waitForConcurrentUpdateCheckAsync = async () => {
|
||||
};
|
||||
|
||||
const getCachedAvailableUpdatesAsync = async (): Promise<UpdateCheckCacheEntry> => {
|
||||
if (env.NO_EXTERNAL_CONNECTION) {
|
||||
return {
|
||||
availableUpdates: [],
|
||||
attemptedAt: Date.now(),
|
||||
checkedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
const cached = await freshUpdateCheckChannel.getAsync();
|
||||
if (cached) return cached;
|
||||
|
||||
|
||||
Reference in new issue
Block a user