Track HiveGrid Cloud planning reference

This commit is contained in:
david committed 2026-06-26 20:48:01 +02:00
1 parent b3d5ea6764
commit e341c5938a
5784 files changed
+1104929 -3

No files matched your search

@@ -0,0 +1,180 @@
import { describe, expect, it } from "vitest";
import { evaluateFlag, getFeatureFlagVariant, type StoredFeatureFlag } from "./evaluate.js";
function mkFlag(overrides: Partial<StoredFeatureFlag> = {}): StoredFeatureFlag {
return {
id: overrides.id ?? "flag_1",
name: overrides.name ?? "test-flag",
enabled: overrides.enabled ?? true,
rules: overrides.rules ?? null,
variants: overrides.variants ?? [],
enabledFrom: overrides.enabledFrom ?? null,
enabledUntil: overrides.enabledUntil ?? null,
parentFlagId: overrides.parentFlagId ?? null,
};
}
function mkLoader(flags: StoredFeatureFlag[]) {
const byKey = new Map<string, StoredFeatureFlag>();
for (const f of flags) {
byKey.set(f.name, f);
byKey.set(f.id, f);
}
return async (key: string) => byKey.get(key) ?? null;
}
describe("evaluateFlag — basics", () => {
it("flag-not-found", async () => {
const r = await evaluateFlag("missing", { flagName: "missing" }, { load: mkLoader([]) });
expect(r).toEqual({ enabled: false, reason: "flag-not-found" });
});
it("disabled", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag" },
{ load: mkLoader([mkFlag({ enabled: false })]) },
);
expect(r.enabled).toBe(false);
expect(r.reason).toBe("disabled");
});
it("enabled with no rules → matched", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag" },
{ load: mkLoader([mkFlag({})]) },
);
expect(r).toEqual({ enabled: true, reason: "matched" });
});
});
describe("evaluateFlag — time window", () => {
it("future enabledFrom → time-window-future", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag", now: new Date("2026-01-01T00:00:00Z") },
{
load: mkLoader([mkFlag({ enabledFrom: new Date("2030-01-01T00:00:00Z") })]),
},
);
expect(r.reason).toBe("time-window-future");
expect(r.enabled).toBe(false);
});
it("past enabledUntil → time-window-expired", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag", now: new Date("2026-01-01T00:00:00Z") },
{
load: mkLoader([mkFlag({ enabledUntil: new Date("2020-01-01T00:00:00Z") })]),
},
);
expect(r.reason).toBe("time-window-expired");
});
});
describe("evaluateFlag — dependency resolution", () => {
it("parent disabled → child returns parent-disabled", async () => {
const parent = mkFlag({ id: "parent_1", name: "parent", enabled: false });
const child = mkFlag({ id: "child_1", name: "child", parentFlagId: "parent_1" });
const r = await evaluateFlag("child", { flagName: "child" }, { load: mkLoader([parent, child]) });
expect(r.reason).toBe("parent-disabled");
expect(r.enabled).toBe(false);
});
it("parent missing → parent-not-found", async () => {
const child = mkFlag({ id: "child_1", name: "child", parentFlagId: "ghost" });
const r = await evaluateFlag("child", { flagName: "child" }, { load: mkLoader([child]) });
expect(r.reason).toBe("parent-not-found");
});
it("parent rules fail → parent-disabled bubbles up", async () => {
const parent = mkFlag({
id: "parent_1",
name: "parent",
rules: { type: "allowlist", userIds: ["other_user"] },
});
const child = mkFlag({ id: "child_1", name: "child", parentFlagId: "parent_1" });
const r = await evaluateFlag(
"child",
{ flagName: "child", userId: "u_self" },
{ load: mkLoader([parent, child]) },
);
expect(r.reason).toBe("parent-disabled");
});
it("parent enabled → child evaluates own rules", async () => {
const parent = mkFlag({ id: "parent_1", name: "parent" });
const child = mkFlag({ id: "child_1", name: "child", parentFlagId: "parent_1" });
const r = await evaluateFlag("child", { flagName: "child" }, { load: mkLoader([parent, child]) });
expect(r.enabled).toBe(true);
});
it("loop guard — child→parent→child returns parent-disabled, no infinite recursion", async () => {
const a = mkFlag({ id: "a", name: "a", parentFlagId: "b" });
const b = mkFlag({ id: "b", name: "b", parentFlagId: "a" });
const r = await evaluateFlag("a", { flagName: "a" }, { load: mkLoader([a, b]) });
expect(r.reason).toBe("parent-disabled");
});
});
describe("evaluateFlag — rules", () => {
it("rule mismatch → no-match", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag", planTier: "free" },
{
load: mkLoader([
mkFlag({ rules: { type: "plan-tier", plans: ["enterprise"] } }),
]),
},
);
expect(r.reason).toBe("no-match");
});
it("rule match → matched", async () => {
const r = await evaluateFlag(
"test-flag",
{ flagName: "test-flag", planTier: "enterprise" },
{
load: mkLoader([
mkFlag({ rules: { type: "plan-tier", plans: ["enterprise"] } }),
]),
},
);
expect(r.enabled).toBe(true);
expect(r.reason).toBe("matched");
});
});
describe("evaluateFlag — variants", () => {
it("single variant always wins", async () => {
const r = await evaluateFlag(
"ab",
{ flagName: "ab", userId: "u" },
{ load: mkLoader([mkFlag({ name: "ab", variants: [{ key: "only" }] })]) },
);
expect(r.variant?.key).toBe("only");
});
it("multiple variants are sticky per user", async () => {
const variants = [
{ key: "A", weight: 1 },
{ key: "B", weight: 1 },
{ key: "C", weight: 1 },
];
const flag = mkFlag({ name: "vv", variants });
const r1 = await evaluateFlag("vv", { flagName: "vv", userId: "u_42" }, { load: mkLoader([flag]) });
const r2 = await evaluateFlag("vv", { flagName: "vv", userId: "u_42" }, { load: mkLoader([flag]) });
expect(r1.variant?.key).toBe(r2.variant?.key);
expect(r1.variant?.key).toMatch(/^[ABC]$/);
});
it("getFeatureFlagVariant returns null when disabled", async () => {
const v = await getFeatureFlagVariant(
"x",
{ flagName: "x" },
{ load: mkLoader([mkFlag({ name: "x", enabled: false, variants: [{ key: "A" }] })]) },
);
expect(v).toBeNull();
});
it("getFeatureFlagVariant returns the variant when matched", async () => {
const v = await getFeatureFlagVariant(
"x",
{ flagName: "x", userId: "u" },
{ load: mkLoader([mkFlag({ name: "x", variants: [{ key: "A" }] })]) },
);
expect(v?.key).toBe("A");
});
});