FN-8417: verify GitHub translation cache reuse
Add route-level coverage for durable GitHub import translation reuse.\n\n- Mock translation and rate-limit services at the GitHub route boundary\n- Verify identical auto-translate requests return cached results without further model or rate-limit cost\n- Confirm cached translations are recorded once\n\nFiles changed:\n .../dashboard/src/__tests__/routes-github.test.ts | 80 ++++++++++++++++++++++\n 1 file changed, 80 insertions(+) Fusion-Task-Id: FN-8417 Fusion-Task-Lineage: 685d753a-d711-4625-809b-14921ce14b6b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -51,6 +51,8 @@ const {
|
||||
mockExecFile,
|
||||
mockGetCachedImportTranslation,
|
||||
mockResolveTargetLocale,
|
||||
mockTranslateText,
|
||||
mockCheckTranslateRateLimit,
|
||||
} = vi.hoisted(() => ({
|
||||
mockPerformUpdateCheck: vi.fn(),
|
||||
mockClearUpdateCheckCache: vi.fn(),
|
||||
@@ -58,8 +60,19 @@ const {
|
||||
mockExecFile: vi.fn(),
|
||||
mockGetCachedImportTranslation: vi.fn(),
|
||||
mockResolveTargetLocale: vi.fn(),
|
||||
mockTranslateText: vi.fn(),
|
||||
mockCheckTranslateRateLimit: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../ai-translate.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../ai-translate.js")>("../ai-translate.js");
|
||||
return {
|
||||
...actual,
|
||||
translateText: mockTranslateText,
|
||||
checkTranslateRateLimit: mockCheckTranslateRateLimit,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../update-check.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../update-check.js")>("../update-check.js");
|
||||
return {
|
||||
@@ -587,6 +600,73 @@ describe("POST /github/issues/fetch", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:GitHubImportTranslate 2026-07-20-00:00:
|
||||
FN-8417 requires the HTTP reopen boundary, not just the translation service, to
|
||||
prove that a second identical auto-translate request uses the durable cache.
|
||||
The route must reserve rate-limit budget only for uncached model work.
|
||||
*/
|
||||
describe("POST /github/issues/auto-translate", () => {
|
||||
let store: TaskStore;
|
||||
|
||||
beforeEach(() => {
|
||||
const translations = new Map<string, {
|
||||
translatedTitle: string;
|
||||
translatedBody: string;
|
||||
detectedLocale: string | null;
|
||||
recordedAt: string;
|
||||
}>();
|
||||
store = createMockStore({
|
||||
getSettings: vi.fn().mockResolvedValue({
|
||||
githubImportAutoTranslate: true,
|
||||
importTranslateTargetLocale: "en",
|
||||
}),
|
||||
getImportTranslation: vi.fn(async (key) => translations.get(JSON.stringify(key)) ?? null),
|
||||
recordImportTranslation: vi.fn(async (key, value) => {
|
||||
translations.set(JSON.stringify(key), {
|
||||
...value,
|
||||
detectedLocale: value.detectedLocale ?? null,
|
||||
recordedAt: "2026-07-20T00:00:00.000Z",
|
||||
});
|
||||
}),
|
||||
});
|
||||
mockTranslateText.mockResolvedValue({ title: "Translated title", body: "Translated body" });
|
||||
mockCheckTranslateRateLimit.mockReturnValue(true);
|
||||
});
|
||||
|
||||
function buildApp() {
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use("/api", createApiRoutes(store));
|
||||
return app;
|
||||
}
|
||||
|
||||
it("auto-translate serves a second identical POST from cache without model or rate-limit cost", async () => {
|
||||
const body = JSON.stringify({
|
||||
owner: "owner",
|
||||
repo: "repo",
|
||||
targetLocale: "en",
|
||||
items: [{ number: 17, title: "Error del servidor", body: "El servidor devuelve un error cuando el usuario intenta guardar los cambios en la configuracion. No se puede completar la operacion porque el sistema no responde. Por favor revise los registros del servidor para mas informacion sobre este problema.", state: "open" }],
|
||||
});
|
||||
|
||||
const first = await REQUEST(buildApp(), "POST", "/api/github/issues/auto-translate", body, {
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
const second = await REQUEST(buildApp(), "POST", "/api/github/issues/auto-translate", body, {
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
|
||||
expect(first.status).toBe(200);
|
||||
expect(second.status).toBe(200);
|
||||
expect(mockTranslateText).toHaveBeenCalledTimes(1);
|
||||
expect(first.body.translations[17]).toEqual({ title: "Translated title", body: "Translated body" });
|
||||
expect(second.body.translations[17]).toEqual({ title: "Translated title", body: "Translated body" });
|
||||
expect(mockCheckTranslateRateLimit).toHaveBeenCalledTimes(1);
|
||||
expect(mockCheckTranslateRateLimit).toHaveBeenCalledWith(expect.any(String), 1);
|
||||
expect(store.recordImportTranslation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /github/issues/import", () => {
|
||||
let store: TaskStore;
|
||||
let getIssueSpy: ReturnType<typeof vi.fn>;
|
||||
|
||||
Reference in New Issue
Block a user