fix(github): preserved aborts during cache refresh

Re-throw ToolAbortError from soft-expired issue and PR synchronous refreshes instead of falling back to stale cached content.

Cover the abort path in github-cache tests.

Fixes #2684
This commit is contained in:
roboomp
2026-06-15 15:43:48 +00:00
parent 47fb727582
commit a2e2a675dc
2 changed files with 40 additions and 0 deletions
@@ -22,6 +22,7 @@ import * as os from "node:os";
import * as path from "node:path";
import { getGithubCacheDbPath, logger } from "@oh-my-pi/pi-utils";
import type { Settings } from "../config/settings";
import { ToolAbortError } from "./tool-errors";
// ────────────────────────────────────────────────────────────────────────────
// Storage layer
@@ -619,6 +620,7 @@ export async function getOrFetchView<T>(options: CacheLookupOptions<T>): Promise
storeResult(authKey, options.repo, options.kind, options.number, options.includeComments, fresh, fetchedAt);
return { ...fresh, status: "refreshed", fetchedAt };
} catch (err) {
if (err instanceof ToolAbortError) throw err;
logger.debug("github cache: synchronous refresh failed; returning stale view", {
err: String(err),
repo: options.repo,
@@ -19,6 +19,7 @@ import {
putCached,
resetForTests as resetCacheForTests,
} from "@oh-my-pi/pi-coding-agent/tools/github-cache";
import { ToolAbortError, throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
import * as git from "@oh-my-pi/pi-coding-agent/utils/git";
const TEST_REPO = "owner/example";
@@ -372,6 +373,43 @@ describe("getOrFetchView (TTL semantics)", () => {
expect(result.rendered).toBe("old");
expect(fetchFresh).toHaveBeenCalledTimes(1);
});
it("propagates aborts during a soft-expired synchronous refresh", async () => {
const settings = Settings.isolated({
"github.cache.softTtlSec": 60,
"github.cache.hardTtlSec": 86400,
});
const controller = new AbortController();
controller.abort();
const fetchFresh = vi.fn(async () => {
throwIfAborted(controller.signal);
return {
rendered: "never",
sourceUrl: undefined,
payload: { number: 53 },
};
});
putCached({
repo: TEST_REPO,
kind: "issue",
number: 53,
includeComments: true,
payload: { number: 53 },
rendered: "stale-after-abort",
fetchedAt: Date.now() - 5 * 60_000,
});
await expect(
getOrFetchView({
repo: TEST_REPO,
kind: "issue",
number: 53,
includeComments: true,
fetchFresh,
settings,
}),
).rejects.toThrow(ToolAbortError);
expect(fetchFresh).toHaveBeenCalledTimes(1);
});
it("keeps PR diff rows stale-first past soft TTL", async () => {
const settings = Settings.isolated({