chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,781 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
// Stub `~/db.server` before importing anything that transitively imports it.
|
||||
// The real module eagerly calls `prisma.$connect()` at singleton construction
|
||||
// (db.server.ts), so loading it under vitest tries to reach localhost:5432
|
||||
// and surfaces as an unhandled rejection that fails the whole shard — even
|
||||
// though no test in this file actually uses the default prisma client.
|
||||
vi.mock("~/db.server", () => ({
|
||||
prisma: {},
|
||||
$replica: {},
|
||||
}));
|
||||
|
||||
import {
|
||||
evaluateGate,
|
||||
makeResolveMollifierFlag,
|
||||
type GateDependencies,
|
||||
type GateInputs,
|
||||
type TripDecision,
|
||||
} from "~/v3/mollifier/mollifierGate.server";
|
||||
import type { DecisionOutcome, DecisionReason } from "~/v3/mollifier/mollifierTelemetry.server";
|
||||
|
||||
// We deliberately don't use vi.fn here. Per repo policy tests shouldn't lean on
|
||||
// mock frameworks for behaviours that are pure functions of the inputs — the
|
||||
// gate is pure decision logic, so a hand-rolled "deps + spy log" wired with
|
||||
// plain closures gives exactly the assertions we need without the indirection.
|
||||
type Spies = {
|
||||
evaluatorCalls: number;
|
||||
logShadowCalls: Array<{ inputs: GateInputs; decision: Extract<TripDecision, { divert: true }> }>;
|
||||
logMollifiedCalls: Array<{
|
||||
inputs: GateInputs;
|
||||
decision: Extract<TripDecision, { divert: true }>;
|
||||
}>;
|
||||
recordDecisionCalls: Array<{
|
||||
outcome: DecisionOutcome;
|
||||
reason?: DecisionReason;
|
||||
enrolled?: boolean;
|
||||
orgId?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
type Toggles = {
|
||||
enabled: boolean;
|
||||
shadow: boolean;
|
||||
flag: boolean;
|
||||
decision: TripDecision;
|
||||
};
|
||||
|
||||
function makeDeps(toggles: Toggles): { deps: GateDependencies; spies: Spies } {
|
||||
const spies: Spies = {
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: [],
|
||||
logMollifiedCalls: [],
|
||||
recordDecisionCalls: [],
|
||||
};
|
||||
const deps: GateDependencies = {
|
||||
isMollifierEnabled: () => toggles.enabled,
|
||||
isShadowModeOn: () => toggles.shadow,
|
||||
resolveOrgFlag: async () => toggles.flag,
|
||||
evaluator: async () => {
|
||||
spies.evaluatorCalls += 1;
|
||||
return toggles.decision;
|
||||
},
|
||||
logShadow: (inputs, decision) => {
|
||||
spies.logShadowCalls.push({ inputs, decision });
|
||||
},
|
||||
logMollified: (inputs, decision) => {
|
||||
spies.logMollifiedCalls.push({ inputs, decision });
|
||||
},
|
||||
recordDecision: (outcome, opts) => {
|
||||
spies.recordDecisionCalls.push({
|
||||
outcome,
|
||||
reason: opts.reason,
|
||||
enrolled: opts.enrolled,
|
||||
orgId: opts.orgId,
|
||||
});
|
||||
},
|
||||
};
|
||||
return { deps, spies };
|
||||
}
|
||||
|
||||
const trippedDecision = {
|
||||
divert: true as const,
|
||||
reason: "per_env_rate" as const,
|
||||
count: 150,
|
||||
threshold: 100,
|
||||
windowMs: 200,
|
||||
holdMs: 500,
|
||||
};
|
||||
|
||||
const passDecision: TripDecision = { divert: false };
|
||||
|
||||
const inputs: GateInputs = {
|
||||
envId: "e1",
|
||||
orgId: "o1",
|
||||
taskId: "t1",
|
||||
orgFeatureFlags: null,
|
||||
};
|
||||
|
||||
// Cascade truth table. Every combination of (enabled, shadow, flag, divert) is
|
||||
// enumerated. `evaluatorCalls` is the expected count, not arbitrary: the gate
|
||||
// short-circuits before the evaluator if `!enabled` or (`!flag && !shadow`).
|
||||
// `expectedReason` is the optional second arg to `recordDecision` — only
|
||||
// divert-true paths attach a reason.
|
||||
type Row = {
|
||||
id: number;
|
||||
enabled: boolean;
|
||||
shadow: boolean;
|
||||
flag: boolean;
|
||||
divert: boolean;
|
||||
expected: {
|
||||
action: "pass_through" | "shadow_log" | "mollify";
|
||||
evaluatorCalls: 0 | 1;
|
||||
logShadowCalls: 0 | 1;
|
||||
logMollifiedCalls: 0 | 1;
|
||||
recordedOutcome: "pass_through" | "shadow_log" | "mollify";
|
||||
expectedReason: "per_env_rate" | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
// 16 rows = 2^4 input combinations. Comment column shows which gate branch
|
||||
// each row exercises so reviewers can map row → code at a glance.
|
||||
const cascade: Row[] = [
|
||||
// enabled=F → kill-switch wins; evaluator+flag never consulted (rows 1-8)
|
||||
{
|
||||
id: 1,
|
||||
enabled: false,
|
||||
shadow: false,
|
||||
flag: false,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
enabled: false,
|
||||
shadow: false,
|
||||
flag: false,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
enabled: false,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
enabled: false,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
enabled: false,
|
||||
shadow: true,
|
||||
flag: false,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
enabled: false,
|
||||
shadow: true,
|
||||
flag: false,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
enabled: false,
|
||||
shadow: true,
|
||||
flag: true,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
enabled: false,
|
||||
shadow: true,
|
||||
flag: true,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
// enabled=T, flag=F, shadow=F → both opt-ins off; evaluator never called (rows 9-10)
|
||||
{
|
||||
id: 9,
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: false,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: false,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
// enabled=T, flag=F, shadow=T → shadow path; divert routes outcome (rows 11-12)
|
||||
{
|
||||
id: 11,
|
||||
enabled: true,
|
||||
shadow: true,
|
||||
flag: false,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
enabled: true,
|
||||
shadow: true,
|
||||
flag: false,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "shadow_log",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 1,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "shadow_log",
|
||||
expectedReason: "per_env_rate",
|
||||
},
|
||||
},
|
||||
// enabled=T, flag=T, shadow=F → mollify path (rows 13-14)
|
||||
{
|
||||
id: 13,
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "mollify",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 1,
|
||||
recordedOutcome: "mollify",
|
||||
expectedReason: "per_env_rate",
|
||||
},
|
||||
},
|
||||
// enabled=T, flag=T, shadow=T → flag wins over shadow (rows 15-16)
|
||||
{
|
||||
id: 15,
|
||||
enabled: true,
|
||||
shadow: true,
|
||||
flag: true,
|
||||
divert: false,
|
||||
expected: {
|
||||
action: "pass_through",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 0,
|
||||
recordedOutcome: "pass_through",
|
||||
expectedReason: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
enabled: true,
|
||||
shadow: true,
|
||||
flag: true,
|
||||
divert: true,
|
||||
expected: {
|
||||
action: "mollify",
|
||||
evaluatorCalls: 1,
|
||||
logShadowCalls: 0,
|
||||
logMollifiedCalls: 1,
|
||||
recordedOutcome: "mollify",
|
||||
expectedReason: "per_env_rate",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe("evaluateGate cascade — exhaustive truth table", () => {
|
||||
it.each(cascade)(
|
||||
"row $id: enabled=$enabled shadow=$shadow flag=$flag divert=$divert → action=$expected.action",
|
||||
async (row) => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: row.enabled,
|
||||
shadow: row.shadow,
|
||||
flag: row.flag,
|
||||
decision: row.divert ? trippedDecision : passDecision,
|
||||
});
|
||||
|
||||
const outcome = await evaluateGate(inputs, deps);
|
||||
|
||||
expect(outcome.action).toBe(row.expected.action);
|
||||
expect(spies.evaluatorCalls).toBe(row.expected.evaluatorCalls);
|
||||
expect(spies.logShadowCalls).toHaveLength(row.expected.logShadowCalls);
|
||||
expect(spies.logMollifiedCalls).toHaveLength(row.expected.logMollifiedCalls);
|
||||
|
||||
// Every evaluation records exactly one decision.
|
||||
expect(spies.recordDecisionCalls).toHaveLength(1);
|
||||
expect(spies.recordDecisionCalls[0].outcome).toBe(row.expected.recordedOutcome);
|
||||
expect(spies.recordDecisionCalls[0].reason).toBe(row.expected.expectedReason);
|
||||
// enrolled label = the resolved per-org flag, now hoisted above the
|
||||
// bypasses so it's set on every decision. orgId is always passed by the
|
||||
// gate; the telemetry layer drops it for non-enrolled (covered in
|
||||
// mollifierDecisionLabels.test.ts).
|
||||
expect(spies.recordDecisionCalls[0].enrolled).toBe(row.flag);
|
||||
expect(spies.recordDecisionCalls[0].orgId).toBe(inputs.orgId);
|
||||
}
|
||||
);
|
||||
|
||||
it("divert log carries the full decision (envId, orgId, taskId, reason, count, threshold, windowMs, holdMs)", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: true,
|
||||
flag: false,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
|
||||
await evaluateGate(inputs, deps);
|
||||
|
||||
expect(spies.logShadowCalls).toEqual([{ inputs, decision: trippedDecision }]);
|
||||
});
|
||||
|
||||
it("mollify log carries the full decision (mirrors shadow log)", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
|
||||
await evaluateGate(inputs, deps);
|
||||
|
||||
expect(spies.logMollifiedCalls).toEqual([{ inputs, decision: trippedDecision }]);
|
||||
});
|
||||
});
|
||||
|
||||
// Hot-path guard: `triggerTask.server.ts` calls `evaluateGate` on every
|
||||
// trigger when `TRIGGER_MOLLIFIER_ENABLED=1`. The per-org override path must resolve
|
||||
// without a Prisma round-trip — otherwise the gate adds a DB query to the
|
||||
// highest-throughput code path in the system (see apps/webapp/CLAUDE.md).
|
||||
describe("resolveMollifierFlag — hot path", () => {
|
||||
it("returns the per-org override when it's set", async () => {
|
||||
const resolve = makeResolveMollifierFlag();
|
||||
|
||||
const enabled = await resolve({
|
||||
envId: "e",
|
||||
orgId: "o",
|
||||
taskId: "t",
|
||||
orgFeatureFlags: { mollifierEnabled: true },
|
||||
});
|
||||
const disabled = await resolve({
|
||||
envId: "e",
|
||||
orgId: "o",
|
||||
taskId: "t",
|
||||
orgFeatureFlags: { mollifierEnabled: false },
|
||||
});
|
||||
|
||||
expect(enabled).toBe(true);
|
||||
expect(disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when the org has no override for the key — no DB query, ever", async () => {
|
||||
// Regression intent: the resolver MUST NOT call `flag()` (which would
|
||||
// query `FeatureFlag` via Prisma) on the trigger hot path. Per-org
|
||||
// rollout via `Organization.featureFlags` JSON is the only enable
|
||||
// path; the fleet-wide kill switch is `TRIGGER_MOLLIFIER_ENABLED`.
|
||||
const resolve = makeResolveMollifierFlag();
|
||||
|
||||
const fromNull = await resolve({
|
||||
envId: "e",
|
||||
orgId: "o",
|
||||
taskId: "t",
|
||||
orgFeatureFlags: null,
|
||||
});
|
||||
const fromUnrelatedKeys = await resolve({
|
||||
envId: "e",
|
||||
orgId: "o",
|
||||
taskId: "t",
|
||||
orgFeatureFlags: { hasAiAccess: true },
|
||||
});
|
||||
|
||||
expect(fromNull).toBe(false);
|
||||
expect(fromUnrelatedKeys).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateGate — fail open on evaluator error", () => {
|
||||
it("treats a throwing evaluator as no-divert (pass_through), and never blocks the trigger", async () => {
|
||||
const spies: Spies = {
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: [],
|
||||
logMollifiedCalls: [],
|
||||
recordDecisionCalls: [],
|
||||
};
|
||||
const deps: Partial<GateDependencies> = {
|
||||
isMollifierEnabled: () => true,
|
||||
isShadowModeOn: () => false,
|
||||
resolveOrgFlag: async () => true,
|
||||
evaluator: async () => {
|
||||
spies.evaluatorCalls += 1;
|
||||
throw new Error("simulated evaluator failure");
|
||||
},
|
||||
logShadow: (inputs, decision) => {
|
||||
spies.logShadowCalls.push({ inputs, decision });
|
||||
},
|
||||
logMollified: (inputs, decision) => {
|
||||
spies.logMollifiedCalls.push({ inputs, decision });
|
||||
},
|
||||
recordDecision: (outcome, opts) => {
|
||||
spies.recordDecisionCalls.push({
|
||||
outcome,
|
||||
reason: opts.reason,
|
||||
enrolled: opts.enrolled,
|
||||
orgId: opts.orgId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const outcome = await evaluateGate(inputs, deps);
|
||||
|
||||
expect(outcome.action).toBe("pass_through");
|
||||
expect(spies.evaluatorCalls).toBe(1);
|
||||
expect(spies.logMollifiedCalls).toHaveLength(0);
|
||||
expect(spies.logShadowCalls).toHaveLength(0);
|
||||
expect(spies.recordDecisionCalls).toHaveLength(1);
|
||||
expect(spies.recordDecisionCalls[0]).toMatchObject({
|
||||
outcome: "pass_through",
|
||||
reason: undefined,
|
||||
enrolled: true,
|
||||
orgId: inputs.orgId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateGate — fail open on resolveOrgFlag error", () => {
|
||||
it("treats org flag as false when resolveOrgFlag throws, and does not block triggers", async () => {
|
||||
const spies: Spies = {
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: [],
|
||||
logMollifiedCalls: [],
|
||||
recordDecisionCalls: [],
|
||||
};
|
||||
const deps: Partial<GateDependencies> = {
|
||||
isMollifierEnabled: () => true,
|
||||
isShadowModeOn: () => false,
|
||||
resolveOrgFlag: async () => {
|
||||
throw new Error("simulated prisma timeout");
|
||||
},
|
||||
evaluator: async () => {
|
||||
spies.evaluatorCalls += 1;
|
||||
return trippedDecision;
|
||||
},
|
||||
logShadow: (inputs, decision) => {
|
||||
spies.logShadowCalls.push({ inputs, decision });
|
||||
},
|
||||
logMollified: (inputs, decision) => {
|
||||
spies.logMollifiedCalls.push({ inputs, decision });
|
||||
},
|
||||
recordDecision: (outcome, opts) => {
|
||||
spies.recordDecisionCalls.push({
|
||||
outcome,
|
||||
reason: opts.reason,
|
||||
enrolled: opts.enrolled,
|
||||
orgId: opts.orgId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const outcome = await evaluateGate(inputs, deps);
|
||||
|
||||
expect(outcome.action).toBe("pass_through");
|
||||
expect(spies.evaluatorCalls).toBe(0);
|
||||
expect(spies.recordDecisionCalls).toHaveLength(1);
|
||||
expect(spies.recordDecisionCalls[0]).toMatchObject({
|
||||
outcome: "pass_through",
|
||||
reason: undefined,
|
||||
enrolled: false,
|
||||
orgId: inputs.orgId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateGate — per-org isolation via Organization.featureFlags", () => {
|
||||
function makeIsolationDeps(resolveOrgFlag: GateDependencies["resolveOrgFlag"]): {
|
||||
deps: Partial<GateDependencies>;
|
||||
spies: Spies;
|
||||
} {
|
||||
const spies: Spies = {
|
||||
evaluatorCalls: 0,
|
||||
logShadowCalls: [],
|
||||
logMollifiedCalls: [],
|
||||
recordDecisionCalls: [],
|
||||
};
|
||||
// Override lifecycle bits and inject the production resolveOrgFlag.
|
||||
// Evaluator returns a fixed tripped decision so the outcome is purely a
|
||||
// function of the flag resolution (which is what we're isolating on).
|
||||
const deps: Partial<GateDependencies> = {
|
||||
isMollifierEnabled: () => true,
|
||||
isShadowModeOn: () => false,
|
||||
resolveOrgFlag,
|
||||
evaluator: async () => {
|
||||
spies.evaluatorCalls += 1;
|
||||
return trippedDecision;
|
||||
},
|
||||
logShadow: (inputs, decision) => {
|
||||
spies.logShadowCalls.push({ inputs, decision });
|
||||
},
|
||||
logMollified: (inputs, decision) => {
|
||||
spies.logMollifiedCalls.push({ inputs, decision });
|
||||
},
|
||||
recordDecision: (outcome, opts) => {
|
||||
spies.recordDecisionCalls.push({
|
||||
outcome,
|
||||
reason: opts.reason,
|
||||
enrolled: opts.enrolled,
|
||||
orgId: opts.orgId,
|
||||
});
|
||||
},
|
||||
};
|
||||
return { deps, spies };
|
||||
}
|
||||
|
||||
// The production resolver — purely in-memory, no Prisma. Mirrors
|
||||
// `defaultGateDependencies.resolveOrgFlag` exactly.
|
||||
const resolve = makeResolveMollifierFlag();
|
||||
|
||||
it("opts in only the org whose featureFlags has mollifierEnabled=true", async () => {
|
||||
const orgA = { ...inputs, orgId: "org_a", orgFeatureFlags: { mollifierEnabled: true } };
|
||||
const orgB = { ...inputs, orgId: "org_b", orgFeatureFlags: { mollifierEnabled: false } };
|
||||
const orgC = { ...inputs, orgId: "org_c", orgFeatureFlags: null };
|
||||
|
||||
const a = makeIsolationDeps(resolve);
|
||||
const b = makeIsolationDeps(resolve);
|
||||
const c = makeIsolationDeps(resolve);
|
||||
|
||||
const [outcomeA, outcomeB, outcomeC] = await Promise.all([
|
||||
evaluateGate(orgA, a.deps),
|
||||
evaluateGate(orgB, b.deps),
|
||||
evaluateGate(orgC, c.deps),
|
||||
]);
|
||||
|
||||
// Only org A's flag is on → only org A mollifies. Orgs B and C never
|
||||
// reach the evaluator because both flag and shadow-mode are off.
|
||||
expect(outcomeA.action).toBe("mollify");
|
||||
expect(outcomeB.action).toBe("pass_through");
|
||||
expect(outcomeC.action).toBe("pass_through");
|
||||
|
||||
expect(a.spies.evaluatorCalls).toBe(1);
|
||||
expect(b.spies.evaluatorCalls).toBe(0);
|
||||
expect(c.spies.evaluatorCalls).toBe(0);
|
||||
|
||||
expect(a.spies.logMollifiedCalls).toHaveLength(1);
|
||||
expect(b.spies.logMollifiedCalls).toHaveLength(0);
|
||||
expect(c.spies.logMollifiedCalls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("another org's beta flags must not opt them into mollifier", async () => {
|
||||
// Org A has mollifier on (plus an unrelated beta).
|
||||
const orgA = {
|
||||
...inputs,
|
||||
orgId: "org_a",
|
||||
orgFeatureFlags: { mollifierEnabled: true, hasComputeAccess: true },
|
||||
};
|
||||
// Org B has *other* betas on but mollifier remains off — keys that gate
|
||||
// compute/AI/query must not bleed across into the mollifier decision.
|
||||
const orgB = {
|
||||
...inputs,
|
||||
orgId: "org_b",
|
||||
orgFeatureFlags: { hasComputeAccess: true, hasAiAccess: true },
|
||||
};
|
||||
|
||||
const a = makeIsolationDeps(resolve);
|
||||
const b = makeIsolationDeps(resolve);
|
||||
|
||||
const outcomeA = await evaluateGate(orgA, a.deps);
|
||||
const outcomeB = await evaluateGate(orgB, b.deps);
|
||||
|
||||
expect(outcomeA.action).toBe("mollify");
|
||||
expect(outcomeB.action).toBe("pass_through");
|
||||
});
|
||||
|
||||
it("orgs without an explicit override stay off — no global FeatureFlag fallback", async () => {
|
||||
// Regression intent: the resolver MUST NOT consult the global
|
||||
// `FeatureFlag` table on the hot path. An org with `orgFeatureFlags`
|
||||
// unset (the default for almost every org during rollout) gets
|
||||
// pass_through, period. The fleet-wide kill switch lives in
|
||||
// `TRIGGER_MOLLIFIER_ENABLED`, not the FeatureFlag table.
|
||||
const orgInherits = { ...inputs, orgId: "org_inherits", orgFeatureFlags: null };
|
||||
const orgEmpty = { ...inputs, orgId: "org_empty", orgFeatureFlags: {} };
|
||||
const orgUnrelated = {
|
||||
...inputs,
|
||||
orgId: "org_unrelated",
|
||||
orgFeatureFlags: { hasAiAccess: true },
|
||||
};
|
||||
|
||||
const inheritsDeps = makeIsolationDeps(resolve);
|
||||
const emptyDeps = makeIsolationDeps(resolve);
|
||||
const unrelatedDeps = makeIsolationDeps(resolve);
|
||||
|
||||
const [outInherits, outEmpty, outUnrelated] = await Promise.all([
|
||||
evaluateGate(orgInherits, inheritsDeps.deps),
|
||||
evaluateGate(orgEmpty, emptyDeps.deps),
|
||||
evaluateGate(orgUnrelated, unrelatedDeps.deps),
|
||||
]);
|
||||
|
||||
expect(outInherits.action).toBe("pass_through");
|
||||
expect(outEmpty.action).toBe("pass_through");
|
||||
expect(outUnrelated.action).toBe("pass_through");
|
||||
// None of these reached the evaluator (flag off, shadow off).
|
||||
expect(inheritsDeps.spies.evaluatorCalls).toBe(0);
|
||||
expect(emptyDeps.spies.evaluatorCalls).toBe(0);
|
||||
expect(unrelatedDeps.spies.evaluatorCalls).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// Bypasses: the three categories of trigger that the mollifier never
|
||||
// intercepts, regardless of the per-org flag or the trip-evaluator decision.
|
||||
describe("evaluateGate — debounce / OTU / triggerAndWait bypasses", () => {
|
||||
it("debounce triggers pass through without invoking the evaluator", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
const outcome = await evaluateGate({ ...inputs, options: { debounce: { key: "k" } } }, deps);
|
||||
expect(outcome).toEqual({ action: "pass_through" });
|
||||
expect(spies.evaluatorCalls).toBe(0);
|
||||
});
|
||||
|
||||
it("oneTimeUseToken triggers pass through without invoking the evaluator", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
const outcome = await evaluateGate(
|
||||
{ ...inputs, options: { oneTimeUseToken: "jwt-otu" } },
|
||||
deps
|
||||
);
|
||||
expect(outcome).toEqual({ action: "pass_through" });
|
||||
expect(spies.evaluatorCalls).toBe(0);
|
||||
});
|
||||
|
||||
it("single triggerAndWait (parentTaskRunId + resumeParentOnCompletion) passes through", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
const outcome = await evaluateGate(
|
||||
{
|
||||
...inputs,
|
||||
options: { parentTaskRunId: "run_parent", resumeParentOnCompletion: true },
|
||||
},
|
||||
deps
|
||||
);
|
||||
expect(outcome).toEqual({ action: "pass_through" });
|
||||
expect(spies.evaluatorCalls).toBe(0);
|
||||
});
|
||||
|
||||
it("parentTaskRunId alone (no resumeParentOnCompletion) does NOT bypass — must be both", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
const outcome = await evaluateGate(
|
||||
{ ...inputs, options: { parentTaskRunId: "run_parent" } },
|
||||
deps
|
||||
);
|
||||
expect(outcome.action).toBe("mollify");
|
||||
expect(spies.evaluatorCalls).toBe(1);
|
||||
});
|
||||
|
||||
it("bypass records pass_through decision (so observability counters stay accurate)", async () => {
|
||||
const { deps, spies } = makeDeps({
|
||||
enabled: true,
|
||||
shadow: false,
|
||||
flag: true,
|
||||
decision: trippedDecision,
|
||||
});
|
||||
await evaluateGate({ ...inputs, options: { debounce: { key: "k" } } }, deps);
|
||||
expect(spies.recordDecisionCalls).toHaveLength(1);
|
||||
expect(spies.recordDecisionCalls[0].outcome).toBe("pass_through");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user