test: fix unit tests

This commit is contained in:
Charlie Lamb
2026-04-21 15:24:55 +01:00
parent 07cb0dfc5b
commit f5ba0564b9
3 changed files with 70 additions and 67 deletions

View File

@@ -126,4 +126,42 @@ describe("runCheckWithRollout", () => {
}),
]);
});
test("returns fail-open fallback when the v2 flow hits redis retry exhaustion", async () => {
mockState.legacyCalls = [];
mockState.v2Calls = [];
mockState.v2Error = Object.assign(new Error("redis retries exhausted"), {
name: "MaxRetriesPerRequestError",
});
mockState.warnCalls = [];
const result = await runCheckWithRollout({
ctx: {
apiVersion: { value: "2025-02-01" },
features: [],
logger: {
warn: (...args: unknown[]) => mockState.warnCalls.push(args),
},
rolloutSnapshot: {
rolloutId: "v2-cache",
enabled: true,
percent: 100,
previousPercent: 0,
changedAt: 1,
customerBucket: 10,
},
} as never,
body: { customer_id: "cus_123", feature_id: "messages" } as never,
requiredBalance: 1,
});
expect(result).toMatchObject({
checkData: null,
response: {
allowed: true,
customer_id: "cus_123",
required_balance: 1,
},
});
});
});

View File

@@ -1,40 +1,5 @@
import { describe, expect, mock, test } from "bun:test";
const mockState = {
v2Calls: [] as Record<string, unknown>[],
v3Calls: [] as Record<string, unknown>[],
v3Error: null as unknown,
queueCalls: [] as Record<string, unknown>[],
queueResponse: null as unknown,
};
mock.module("@/internal/balances/track/runTrackV2.js", () => ({
runTrackV2: async (args: Record<string, unknown>) => {
mockState.v2Calls.push(args);
return { source: "v2" };
},
}));
mock.module("@/internal/balances/track/v3/runTrackV3.js", () => ({
runTrackV3: async (args: Record<string, unknown>) => {
mockState.v3Calls.push(args);
if (mockState.v3Error) throw mockState.v3Error;
return { source: "v3" };
},
}));
mock.module("@/internal/balances/track/utils/queueTrack.js", () => ({
queueTrack: async (args: Record<string, unknown>) => {
mockState.queueCalls.push(args);
return mockState.queueResponse;
},
}));
import {
runTrackWithRollout,
shouldUseTrackV3,
} from "@/internal/balances/track/runTrackWithRollout.js";
import { describe, expect, test } from "bun:test";
import { shouldUseTrackV3 } from "@/internal/balances/track/runTrackWithRollout.js";
describe("runTrackWithRollout", () => {
test("keeps track v3 disabled when rollout is off", () => {
@@ -63,34 +28,4 @@ describe("runTrackWithRollout", () => {
}),
).toBe(true);
});
test("queues track fallback when track v3 hits a retryable error", async () => {
mockState.v2Calls = [];
mockState.v3Calls = [];
mockState.queueCalls = [];
mockState.v3Error = Object.assign(new Error("redis retries exhausted"), {
name: "MaxRetriesPerRequestError",
});
mockState.queueResponse = { queued: true };
const result = await runTrackWithRollout({
ctx: {
rolloutSnapshot: {
rolloutId: "v2-cache",
enabled: true,
percent: 100,
previousPercent: 0,
changedAt: 1,
customerBucket: 5,
},
} as never,
body: { customer_id: "cus_123", feature_id: "messages" } as never,
featureDeductions: [],
});
expect(result as unknown).toEqual({ queued: true });
expect(mockState.v3Calls).toHaveLength(1);
expect(mockState.v2Calls).toHaveLength(0);
expect(mockState.queueCalls).toHaveLength(1);
});
});

View File

@@ -0,0 +1,30 @@
import { describe, expect, test } from "bun:test";
import { isRetryableFullSubjectRolloutError } from "@/internal/misc/rollouts/fullSubjectRolloutUtils.js";
describe("fullSubjectRolloutUtils", () => {
test("treats retryable DB errors as retryable rollout errors", () => {
expect(
isRetryableFullSubjectRolloutError({
error: Object.assign(new Error("statement timeout"), { code: "57014" }),
}),
).toBe(true);
});
test("treats ioredis max retries as a retryable rollout error", () => {
expect(
isRetryableFullSubjectRolloutError({
error: Object.assign(new Error("redis retries exhausted"), {
name: "MaxRetriesPerRequestError",
}),
}),
).toBe(true);
});
test("does not treat application errors as retryable rollout errors", () => {
expect(
isRetryableFullSubjectRolloutError({
error: Object.assign(new Error("invalid request"), { code: "400" }),
}),
).toBe(false);
});
});