mirror of https://github.com/garrytan/gstack.git
83 lines
3.0 KiB
TypeScript
83 lines
3.0 KiB
TypeScript
/**
|
|
* Regression coverage for issue #1519 — image-generation calls were timing
|
|
* out at a hardcoded 120s with no CLI override. The fix raised the default
|
|
* and exposed a per-invocation override via `--api-timeout`.
|
|
*
|
|
* These tests pin the contract that the constant exports at the new value
|
|
* and that the `timeoutMs` param is honored by the AbortController path.
|
|
*/
|
|
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
import fs from "fs";
|
|
import os from "os";
|
|
import path from "path";
|
|
import { generateVariant } from "../src/variants";
|
|
import { DEFAULT_IMAGE_GEN_TIMEOUT_MS } from "../src/constants";
|
|
|
|
describe("DEFAULT_IMAGE_GEN_TIMEOUT_MS", () => {
|
|
test("default is 300_000ms (5min) — see issue #1519", () => {
|
|
expect(DEFAULT_IMAGE_GEN_TIMEOUT_MS).toBe(300_000);
|
|
});
|
|
});
|
|
|
|
describe("generateVariant timeoutMs override", () => {
|
|
let tmpDir: string;
|
|
let outputPath: string;
|
|
|
|
beforeEach(() => {
|
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "api-timeout-"));
|
|
outputPath = path.join(tmpDir, "variant.png");
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
});
|
|
|
|
test("aborts after explicit timeoutMs when fetch never resolves", async () => {
|
|
// Stub fetch that waits for the signal to abort, then throws AbortError.
|
|
const fetchFn = (async (_input: any, init?: any) => {
|
|
const signal = init?.signal as AbortSignal | undefined;
|
|
await new Promise<void>((_resolve, reject) => {
|
|
if (!signal) return;
|
|
signal.addEventListener("abort", () => {
|
|
const err: any = new Error("aborted");
|
|
err.name = "AbortError";
|
|
reject(err);
|
|
});
|
|
});
|
|
return new Response("never reached");
|
|
}) as typeof globalThis.fetch;
|
|
|
|
const t0 = Date.now();
|
|
const result = await generateVariant(
|
|
"fake-key", "prompt", outputPath, "1024x1024", "high", fetchFn, 200,
|
|
);
|
|
const elapsed = Date.now() - t0;
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.error).toBe("Timeout (200ms)");
|
|
// Was aborted by the 200ms timer, not by exponential-backoff retry chain
|
|
expect(elapsed).toBeLessThan(2_000);
|
|
});
|
|
|
|
test("default timeoutMs is the shared constant when omitted", async () => {
|
|
// 1x1 transparent PNG, base64
|
|
const TINY_PNG_BASE64 =
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=";
|
|
const fetchFn = (async () =>
|
|
new Response(
|
|
JSON.stringify({
|
|
output: [{ type: "image_generation_call", result: TINY_PNG_BASE64 }],
|
|
}),
|
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
)) as typeof globalThis.fetch;
|
|
|
|
// Should succeed using the default timeout — fetch resolves instantly here,
|
|
// so the timeoutMs value only matters for the AbortController setup not firing.
|
|
const result = await generateVariant(
|
|
"fake-key", "prompt", outputPath, "1024x1024", "high", fetchFn,
|
|
);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|