438 tests per windows builds now
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
canUserBuild,
|
||||
getBuildRateLimitStats,
|
||||
recordBuildEnd,
|
||||
recordBuildStart,
|
||||
} from "./build-rate-limit";
|
||||
|
||||
// Module state is process-global. Each test uses a unique userId so concurrent
|
||||
// or sequential tests don't leak state into each other, and always pairs every
|
||||
// recordBuildStart with a recordBuildEnd in a try/finally to keep global counts
|
||||
// clean for unrelated tests.
|
||||
|
||||
let nextUserId = 1_000_000;
|
||||
function uniqueUser(): number {
|
||||
nextUserId += 1;
|
||||
return nextUserId;
|
||||
}
|
||||
|
||||
describe("canUserBuild", () => {
|
||||
test("allows a fresh user", () => {
|
||||
expect(canUserBuild(uniqueUser())).toEqual({ allowed: true });
|
||||
});
|
||||
|
||||
test("blocks user once their per-user concurrent limit is reached", () => {
|
||||
const userId = uniqueUser();
|
||||
const started: number[] = [];
|
||||
try {
|
||||
// Saturate the per-user concurrent slot (default config: 1).
|
||||
while (true) {
|
||||
const decision = canUserBuild(userId);
|
||||
if (!decision.allowed) break;
|
||||
recordBuildStart(userId);
|
||||
started.push(userId);
|
||||
if (started.length > 20) throw new Error("unexpected: never hit per-user cap");
|
||||
}
|
||||
const blocked = canUserBuild(userId);
|
||||
expect(blocked.allowed).toBe(false);
|
||||
expect(blocked.reason).toMatch(/concurrent per user/i);
|
||||
} finally {
|
||||
for (const u of started) recordBuildEnd(u);
|
||||
}
|
||||
});
|
||||
|
||||
test("releases the slot after recordBuildEnd", () => {
|
||||
const userId = uniqueUser();
|
||||
recordBuildStart(userId);
|
||||
try {
|
||||
const mid = canUserBuild(userId);
|
||||
// May be blocked depending on per-user cap; either way recordBuildEnd must restore capacity.
|
||||
if (!mid.allowed) {
|
||||
recordBuildEnd(userId);
|
||||
expect(canUserBuild(userId).allowed).toBe(true);
|
||||
// Re-start so the finally block's recordBuildEnd matches a started build.
|
||||
recordBuildStart(userId);
|
||||
}
|
||||
} finally {
|
||||
recordBuildEnd(userId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("recordBuildStart / recordBuildEnd", () => {
|
||||
test("globalActive increases then decreases symmetrically", () => {
|
||||
const userId = uniqueUser();
|
||||
const before = getBuildRateLimitStats().globalActive;
|
||||
recordBuildStart(userId);
|
||||
try {
|
||||
expect(getBuildRateLimitStats().globalActive).toBe(before + 1);
|
||||
} finally {
|
||||
recordBuildEnd(userId);
|
||||
}
|
||||
expect(getBuildRateLimitStats().globalActive).toBe(before);
|
||||
});
|
||||
|
||||
test("recordBuildEnd on a user with no active builds does not underflow", () => {
|
||||
const userId = uniqueUser();
|
||||
const before = getBuildRateLimitStats().globalActive;
|
||||
// No matching start. Should be a no-op for global count.
|
||||
recordBuildEnd(userId);
|
||||
expect(getBuildRateLimitStats().globalActive).toBe(before);
|
||||
});
|
||||
|
||||
test("multiple starts accumulate active count per user", () => {
|
||||
const userId = uniqueUser();
|
||||
recordBuildStart(userId);
|
||||
recordBuildStart(userId);
|
||||
try {
|
||||
const stats = getBuildRateLimitStats();
|
||||
const entry = stats.perUser.find((u) => u.userId === userId);
|
||||
expect(entry).toBeDefined();
|
||||
expect(entry!.active).toBe(2);
|
||||
expect(entry!.recentCount).toBe(2);
|
||||
} finally {
|
||||
recordBuildEnd(userId);
|
||||
recordBuildEnd(userId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBuildRateLimitStats", () => {
|
||||
test("omits users with no recent or active builds", () => {
|
||||
const userId = uniqueUser();
|
||||
recordBuildStart(userId);
|
||||
recordBuildEnd(userId);
|
||||
// Without waiting an hour the timestamp is still 'recent', so the entry stays —
|
||||
// but stats should still report it as active=0 if anything is reported at all.
|
||||
const stats = getBuildRateLimitStats();
|
||||
const entry = stats.perUser.find((u) => u.userId === userId);
|
||||
if (entry) {
|
||||
expect(entry.active).toBe(0);
|
||||
expect(entry.recentCount).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user