@@ -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