import { describe, expect, it } from "@effect/vitest"; import { Data, Effect } from "effect"; import type { ExecutionEngine } from "@executor-js/execution"; import { makeExecutionRateLimiter } from "./execution-rate-limit"; import { RATE_LIMIT_BLOCKED_MESSAGE } from "./execution-limit-messages"; const ORG = "org_test"; /** Stands in for whatever the real lookups fail with (Autumn down, DO down). */ class UpstreamDownError extends Data.TaggedError("UpstreamDownError")<{ readonly which: string; }> {} // A stand-in engine: `execute` resolves to a marker, so a test tells an allowed // execution (marker) from a blocked one (the gate's error result) by which of // the two came back. A blocked decision never reaches the engine at all. const engineStub: ExecutionEngine = { execute: () => Effect.succeed({ result: "ran" }), executeWithPause: () => Effect.succeed({ status: "completed", result: { result: "ran" } }), resume: () => Effect.succeed(null), getPausedExecution: () => Effect.succeed(null), pausedExecutionCount: () => Effect.succeed(0), hasPausedExecutions: () => Effect.succeed(false), getDescription: Effect.succeed("stub"), }; /** Counter that hands out a caller-controlled sequence of counts. */ const countingIncrement = (counts: ReadonlyArray) => { let calls = 0; return () => Effect.succeed(counts[Math.min(calls++, counts.length - 1)] ?? 0); }; const runExecute = (limiter: ReturnType) => Effect.runPromise( limiter.decorate(ORG, engineStub).execute("code", { // Never invoked: the stub ignores it, and a blocked execution never runs. onElicitation: () => Effect.die("elicitation is not exercised here"), }), ); describe("execution rate limiter — paid exemption", () => { it("allows executions under the cap without consulting the exemption", async () => { let exemptionCalls = 0; const limiter = makeExecutionRateLimiter(countingIncrement([1]), { limit: 10, isExempt: () => { exemptionCalls += 1; return Effect.succeed(false); }, }); expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); // The whole point of resolving lazily: the common path costs no lookup. expect(exemptionCalls).toBe(0); }); it("blocks a non-exempt org over the cap", async () => { const limiter = makeExecutionRateLimiter(countingIncrement([11]), { limit: 10, isExempt: () => Effect.succeed(false), }); expect(await runExecute(limiter)).toMatchObject({ result: null, error: RATE_LIMIT_BLOCKED_MESSAGE, }); }); it("allows an exempt org over the cap", async () => { const limiter = makeExecutionRateLimiter(countingIncrement([11]), { limit: 10, isExempt: () => Effect.succeed(true), }); expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); }); it("caches the exemption so a paid org past the cap looks it up once", async () => { let exemptionCalls = 0; const limiter = makeExecutionRateLimiter(countingIncrement([11, 12, 13]), { limit: 10, exemptionTtlMs: 60_000, now: () => 1_000, isExempt: () => { exemptionCalls += 1; return Effect.succeed(true); }, }); for (let i = 0; i < 3; i += 1) expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); expect(exemptionCalls).toBe(1); }); it("re-resolves once the cached exemption expires", async () => { let exemptionCalls = 0; let clock = 1_000; const limiter = makeExecutionRateLimiter(countingIncrement([11, 12]), { limit: 10, exemptionTtlMs: 1_000, now: () => clock, isExempt: () => { exemptionCalls += 1; return Effect.succeed(true); }, }); await runExecute(limiter); clock += 5_000; await runExecute(limiter); expect(exemptionCalls).toBe(2); }); it("blocks when the exemption cannot be resolved and nothing is cached", async () => { // Deliberately NOT fail-open: an unresolvable exemption during an Autumn // outage must not switch the backstop off, which is the one scenario it // exists to cover. const limiter = makeExecutionRateLimiter(countingIncrement([11]), { limit: 10, isExempt: () => Effect.fail(new UpstreamDownError({ which: "autumn" })), }); expect(await runExecute(limiter)).toMatchObject({ result: null, error: RATE_LIMIT_BLOCKED_MESSAGE, }); }); it("honours a stale exemption when a later lookup fails", async () => { let clock = 1_000; let shouldFail = false; const limiter = makeExecutionRateLimiter(countingIncrement([11, 12]), { limit: 10, exemptionTtlMs: 1_000, now: () => clock, isExempt: () => shouldFail ? Effect.fail(new UpstreamDownError({ which: "autumn" })) : Effect.succeed(true), }); expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); // Cache expires and Autumn is now unreachable: the known-paid org keeps // running rather than getting blocked mid-workload by a blip. clock += 5_000; shouldFail = true; expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); }); it("fails open when the counter itself is unreachable", async () => { const limiter = makeExecutionRateLimiter( () => Effect.fail(new UpstreamDownError({ which: "counter DO" })), { limit: 10, isExempt: () => Effect.succeed(false), }, ); expect(await runExecute(limiter)).toMatchObject({ result: "ran" }); }); it("applies the cap when no exemption predicate is wired", async () => { const limiter = makeExecutionRateLimiter(countingIncrement([11]), { limit: 10 }); expect(await runExecute(limiter)).toMatchObject({ result: null, error: RATE_LIMIT_BLOCKED_MESSAGE, }); }); });