104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
BackgroundBuildReconciler,
|
|
DEFAULT_BUILD_RECONCILE_MS,
|
|
DEFAULT_BUILD_RECONCILE_TIMEOUT_MS,
|
|
MAX_BUILD_RECONCILE_TIMEOUT_MS,
|
|
MAX_BUILD_RECONCILE_MS,
|
|
buildReconcileIntervalMs,
|
|
buildReconcileTimeoutMs,
|
|
} from "../../server/build-reconciler";
|
|
|
|
describe("build reconciliation interval", () => {
|
|
test("uses positive finite configured intervals and defaults invalid values", () => {
|
|
expect(buildReconcileIntervalMs("1500")).toBe(1500);
|
|
expect(buildReconcileIntervalMs(String(MAX_BUILD_RECONCILE_MS))).toBe(
|
|
MAX_BUILD_RECONCILE_MS,
|
|
);
|
|
for (const value of [
|
|
undefined,
|
|
"",
|
|
"0",
|
|
"-1",
|
|
"Infinity",
|
|
"nope",
|
|
String(MAX_BUILD_RECONCILE_MS + 1),
|
|
])
|
|
expect(buildReconcileIntervalMs(value)).toBe(DEFAULT_BUILD_RECONCILE_MS);
|
|
});
|
|
});
|
|
|
|
describe("background build reconciliation", () => {
|
|
test("does not overlap a timed-out scan and resumes after it settles", async () => {
|
|
const scans: AbortSignal[] = [];
|
|
let expire!: () => void;
|
|
let releaseFirst!: () => void;
|
|
let firstSettled!: () => void;
|
|
const firstReleased = new Promise<void>((resolve) => {
|
|
releaseFirst = resolve;
|
|
});
|
|
const settled = new Promise<void>((resolve) => {
|
|
firstSettled = resolve;
|
|
});
|
|
const failures: unknown[] = [];
|
|
const reconciler = new BackgroundBuildReconciler({
|
|
runner: {
|
|
reconcilePendingBuilds: async ({ signal } = {}) => {
|
|
scans.push(signal!);
|
|
if (scans.length === 1) {
|
|
await firstReleased;
|
|
firstSettled();
|
|
}
|
|
},
|
|
},
|
|
timeoutMs: 100,
|
|
onFailure: (error) => failures.push(error),
|
|
setTimeout: ((callback: () => void) => {
|
|
expire = callback;
|
|
return 0 as unknown as ReturnType<typeof setTimeout>;
|
|
}) as typeof setTimeout,
|
|
clearTimeout: (() => {}) as typeof clearTimeout,
|
|
});
|
|
|
|
reconciler.tick();
|
|
reconciler.tick();
|
|
expect(scans).toHaveLength(1);
|
|
|
|
expire();
|
|
expect(scans[0]!.aborted).toBe(true);
|
|
reconciler.tick();
|
|
await Promise.resolve();
|
|
|
|
expect(scans).toHaveLength(1);
|
|
expect(failures).toHaveLength(1);
|
|
|
|
releaseFirst();
|
|
await settled;
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
reconciler.tick();
|
|
await Promise.resolve();
|
|
|
|
expect(scans).toHaveLength(2);
|
|
});
|
|
|
|
test("uses positive finite configured timeouts and defaults invalid values", () => {
|
|
expect(buildReconcileTimeoutMs("1500")).toBe(1500);
|
|
expect(
|
|
buildReconcileTimeoutMs(String(MAX_BUILD_RECONCILE_TIMEOUT_MS)),
|
|
).toBe(MAX_BUILD_RECONCILE_TIMEOUT_MS);
|
|
for (const value of [
|
|
undefined,
|
|
"",
|
|
"0",
|
|
"-1",
|
|
"Infinity",
|
|
"nope",
|
|
String(MAX_BUILD_RECONCILE_TIMEOUT_MS + 1),
|
|
])
|
|
expect(buildReconcileTimeoutMs(value)).toBe(
|
|
DEFAULT_BUILD_RECONCILE_TIMEOUT_MS,
|
|
);
|
|
});
|
|
});
|