fix(qqbot): bound upload cache expiry

This commit is contained in:
Peter Steinberger
2026-05-30 12:46:56 -04:00
parent 031583e8f5
commit bfceffa2f7
2 changed files with 46 additions and 3 deletions

View File

@@ -0,0 +1,34 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { computeFileHash, getCachedFileInfo, setCachedFileInfo } from "./upload-cache.js";
describe("qqbot upload-cache", () => {
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it("reuses cached file info before expiry", () => {
const hash = computeFileHash("qqbot-cache-hit");
setCachedFileInfo(hash, "group", "target-hit", 1, "file-info-hit", "uuid-hit", 3600);
expect(getCachedFileInfo(hash, "group", "target-hit", 1)).toBe("file-info-hit");
});
it("drops cached file info when the current clock is invalid", () => {
const hash = computeFileHash("qqbot-invalid-clock");
setCachedFileInfo(hash, "group", "target-invalid-clock", 1, "file-info-invalid", "uuid", 3600);
vi.spyOn(Date, "now").mockReturnValue(Number.NaN);
expect(getCachedFileInfo(hash, "group", "target-invalid-clock", 1)).toBeNull();
});
it("does not cache file info when ttl expiry exceeds the Date range", () => {
vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
const hash = computeFileHash("qqbot-overflow");
setCachedFileInfo(hash, "group", "target-overflow", 1, "file-info-overflow", "uuid", 3600);
expect(getCachedFileInfo(hash, "group", "target-overflow", 1)).toBeNull();
});
});

View File

@@ -4,6 +4,10 @@
*/
import * as crypto from "node:crypto";
import {
isFutureDateTimestampMs,
resolveExpiresAtMsFromDurationSeconds,
} from "openclaw/plugin-sdk/number-runtime";
import type { ChatScope } from "../types.js";
import { debugLog } from "./log.js";
@@ -46,7 +50,7 @@ export function getCachedFileInfo(
return null;
}
if (Date.now() >= entry.expiresAt) {
if (!isFutureDateTimestampMs(entry.expiresAt)) {
cache.delete(key);
return null;
}
@@ -68,7 +72,7 @@ export function setCachedFileInfo(
if (cache.size >= MAX_CACHE_SIZE) {
const now = Date.now();
for (const [k, v] of cache) {
if (now >= v.expiresAt) {
if (!isFutureDateTimestampMs(v.expiresAt, { nowMs: now })) {
cache.delete(k);
}
}
@@ -83,11 +87,16 @@ export function setCachedFileInfo(
const key = buildCacheKey(contentHash, scope, targetId, fileType);
const safetyMargin = 60;
const effectiveTtl = Math.max(ttl - safetyMargin, 10);
const expiresAt = resolveExpiresAtMsFromDurationSeconds(effectiveTtl);
if (expiresAt === undefined) {
cache.delete(key);
return;
}
cache.set(key, {
fileInfo,
fileUuid,
expiresAt: Date.now() + effectiveTtl * 1000,
expiresAt,
});
debugLog(