Skip to content

Commit 49ad584

Browse files
committed
fix: sanitize flags in demo
1 parent 6ccf7ef commit 49ad584

File tree

10 files changed

+715
-225
lines changed

10 files changed

+715
-225
lines changed

apps/api/src/routes/public/flags.test.ts

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,3 +1024,250 @@ describe("rollout stability", () => {
10241024
}
10251025
});
10261026
});
1027+
1028+
describe("evaluateFlag - target groups", () => {
1029+
it("enables flag when user matches a target group rule", () => {
1030+
const flag = {
1031+
key: "test-flag",
1032+
type: "boolean",
1033+
defaultValue: false,
1034+
payload: { feature: "beta" },
1035+
rules: [],
1036+
resolvedTargetGroups: [
1037+
{
1038+
id: "group-1",
1039+
rules: [
1040+
{
1041+
type: "user_id",
1042+
operator: "equals",
1043+
value: "vip-user",
1044+
enabled: true,
1045+
batch: false,
1046+
},
1047+
],
1048+
},
1049+
],
1050+
};
1051+
1052+
const result = evaluateFlag(flag, { userId: "vip-user" });
1053+
expect(result.enabled).toBe(true);
1054+
expect(result.reason).toBe("TARGET_GROUP_MATCH");
1055+
expect(result.payload).toEqual({ feature: "beta" });
1056+
});
1057+
1058+
it("disables flag when target group rule has enabled: false", () => {
1059+
const flag = {
1060+
key: "test-flag",
1061+
type: "boolean",
1062+
defaultValue: true,
1063+
payload: { feature: "beta" },
1064+
rules: [],
1065+
resolvedTargetGroups: [
1066+
{
1067+
id: "blocklist-group",
1068+
rules: [
1069+
{
1070+
type: "email",
1071+
operator: "contains",
1072+
value: "@competitor.com",
1073+
enabled: false,
1074+
batch: false,
1075+
},
1076+
],
1077+
},
1078+
],
1079+
};
1080+
1081+
const result = evaluateFlag(flag, { email: "[email protected]" });
1082+
expect(result.enabled).toBe(false);
1083+
expect(result.reason).toBe("TARGET_GROUP_MATCH");
1084+
expect(result.payload).toBeNull();
1085+
});
1086+
1087+
it("falls back to default when no target group rules match", () => {
1088+
const flag = {
1089+
key: "test-flag",
1090+
type: "boolean",
1091+
defaultValue: true,
1092+
payload: null,
1093+
rules: [],
1094+
resolvedTargetGroups: [
1095+
{
1096+
id: "beta-users",
1097+
rules: [
1098+
{
1099+
type: "user_id",
1100+
operator: "equals",
1101+
value: "beta-tester",
1102+
enabled: true,
1103+
batch: false,
1104+
},
1105+
],
1106+
},
1107+
],
1108+
};
1109+
1110+
const result = evaluateFlag(flag, { userId: "regular-user" });
1111+
expect(result.enabled).toBe(true);
1112+
expect(result.reason).toBe("BOOLEAN_DEFAULT");
1113+
});
1114+
1115+
it("prioritizes direct flag rules over target group rules", () => {
1116+
const flag = {
1117+
key: "test-flag",
1118+
type: "boolean",
1119+
defaultValue: false,
1120+
payload: { source: "direct" },
1121+
rules: [
1122+
{
1123+
type: "user_id",
1124+
operator: "equals",
1125+
value: "admin",
1126+
enabled: true,
1127+
batch: false,
1128+
},
1129+
],
1130+
resolvedTargetGroups: [
1131+
{
1132+
id: "beta-group",
1133+
rules: [
1134+
{
1135+
type: "user_id",
1136+
operator: "equals",
1137+
value: "admin",
1138+
enabled: false,
1139+
batch: false,
1140+
},
1141+
],
1142+
},
1143+
],
1144+
};
1145+
1146+
const result = evaluateFlag(flag, { userId: "admin" });
1147+
expect(result.enabled).toBe(true);
1148+
expect(result.reason).toBe("USER_RULE_MATCH");
1149+
});
1150+
1151+
it("checks multiple target groups until match found", () => {
1152+
const flag = {
1153+
key: "test-flag",
1154+
type: "boolean",
1155+
defaultValue: false,
1156+
payload: { feature: "exclusive" },
1157+
rules: [],
1158+
resolvedTargetGroups: [
1159+
{
1160+
id: "group-1",
1161+
rules: [
1162+
{
1163+
type: "email",
1164+
operator: "ends_with",
1165+
value: "@company-a.com",
1166+
enabled: true,
1167+
batch: false,
1168+
},
1169+
],
1170+
},
1171+
{
1172+
id: "group-2",
1173+
rules: [
1174+
{
1175+
type: "email",
1176+
operator: "ends_with",
1177+
value: "@company-b.com",
1178+
enabled: true,
1179+
batch: false,
1180+
},
1181+
],
1182+
},
1183+
],
1184+
};
1185+
1186+
const resultA = evaluateFlag(flag, { email: "[email protected]" });
1187+
expect(resultA.enabled).toBe(true);
1188+
expect(resultA.reason).toBe("TARGET_GROUP_MATCH");
1189+
1190+
const resultB = evaluateFlag(flag, { email: "[email protected]" });
1191+
expect(resultB.enabled).toBe(true);
1192+
expect(resultB.reason).toBe("TARGET_GROUP_MATCH");
1193+
1194+
const resultOther = evaluateFlag(flag, { email: "[email protected]" });
1195+
expect(resultOther.enabled).toBe(false);
1196+
expect(resultOther.reason).toBe("BOOLEAN_DEFAULT");
1197+
});
1198+
1199+
it("handles empty resolvedTargetGroups array", () => {
1200+
const flag = {
1201+
key: "test-flag",
1202+
type: "boolean",
1203+
defaultValue: true,
1204+
payload: null,
1205+
rules: [],
1206+
resolvedTargetGroups: [],
1207+
};
1208+
1209+
const result = evaluateFlag(flag, { userId: "test" });
1210+
expect(result.enabled).toBe(true);
1211+
expect(result.reason).toBe("BOOLEAN_DEFAULT");
1212+
});
1213+
1214+
it("handles target group with batch rules", () => {
1215+
const flag = {
1216+
key: "test-flag",
1217+
type: "boolean",
1218+
defaultValue: false,
1219+
payload: { access: "granted" },
1220+
rules: [],
1221+
resolvedTargetGroups: [
1222+
{
1223+
id: "allowlist-group",
1224+
rules: [
1225+
{
1226+
type: "user_id",
1227+
operator: "in",
1228+
batch: true,
1229+
batchValues: ["user-1", "user-2", "user-3"],
1230+
enabled: true,
1231+
},
1232+
],
1233+
},
1234+
],
1235+
};
1236+
1237+
expect(evaluateFlag(flag, { userId: "user-1" }).enabled).toBe(true);
1238+
expect(evaluateFlag(flag, { userId: "user-2" }).enabled).toBe(true);
1239+
expect(evaluateFlag(flag, { userId: "user-4" }).enabled).toBe(false);
1240+
});
1241+
1242+
it("handles target group with property rules", () => {
1243+
const flag = {
1244+
key: "test-flag",
1245+
type: "boolean",
1246+
defaultValue: false,
1247+
payload: { tier: "premium" },
1248+
rules: [],
1249+
resolvedTargetGroups: [
1250+
{
1251+
id: "premium-users",
1252+
rules: [
1253+
{
1254+
type: "property",
1255+
operator: "in",
1256+
field: "plan",
1257+
values: ["pro", "enterprise"],
1258+
enabled: true,
1259+
batch: false,
1260+
},
1261+
],
1262+
},
1263+
],
1264+
};
1265+
1266+
const resultPro = evaluateFlag(flag, { properties: { plan: "pro" } });
1267+
expect(resultPro.enabled).toBe(true);
1268+
expect(resultPro.reason).toBe("TARGET_GROUP_MATCH");
1269+
1270+
const resultFree = evaluateFlag(flag, { properties: { plan: "free" } });
1271+
expect(resultFree.enabled).toBe(false);
1272+
});
1273+
});

0 commit comments

Comments
 (0)