@@ -1031,6 +1031,205 @@ describe("isEligibleForStrategy", () => {
10311031 ) . toBe ( false ) ;
10321032 } ) ;
10331033
1034+ // ── semver_equal ─────────────────────────────────────────────────────
1035+
1036+ it . each ( [
1037+ [ "1.0.0" , "1.0.0" , true ] ,
1038+ [ "1.0.0" , "2.0.0" , false ] ,
1039+ [ "1.2.3" , "1.2.3" , true ] ,
1040+ [ "2.0.0" , "1.0.0" , false ] ,
1041+ [ "1.0.0-alpha" , "1.0.0-alpha" , true ] ,
1042+ [ "1.0.0-alpha" , "1.0.0-beta" , false ] ,
1043+ [ "1.0.0-alpha" , "1.0.0" , false ] ,
1044+ [ "1.0.0+build.1" , "1.0.0+build.2" , true ] , // build metadata ignored per semver spec
1045+ [ "1.0.0+build.1" , "1.0.0" , true ] , // build metadata ignored
1046+ [ "0.0.1" , "0.0.1" , true ] ,
1047+ [ "10.20.30" , "10.20.30" , true ] ,
1048+ [ "1.0.0-alpha.1" , "1.0.0-alpha.1" , true ] ,
1049+ [ "1.0.0-alpha.1" , "1.0.0-alpha.2" , false ] ,
1050+ ] ) (
1051+ "when field is '%s' and rule value is '%s', then it should return '%s' for operator semver_equal" ,
1052+ ( userValue , ruleValue , expected ) => {
1053+ const rules : Rule [ ] = [
1054+ {
1055+ operator : "semver_equal" ,
1056+ field : "version" ,
1057+ value : ruleValue ,
1058+ } ,
1059+ ] ;
1060+
1061+ expect (
1062+ isEligibleForStrategy ( rules , { __id : "yo" , version : userValue } )
1063+ ) . toBe ( expected ) ;
1064+ }
1065+ ) ;
1066+
1067+ it ( "semver_equal returns false for invalid user version" , ( ) => {
1068+ const rules : Rule [ ] = [
1069+ { operator : "semver_equal" , field : "version" , value : "1.0.0" } ,
1070+ ] ;
1071+ expect (
1072+ isEligibleForStrategy ( rules , { __id : "yo" , version : "not-semver" } )
1073+ ) . toBe ( false ) ;
1074+ } ) ;
1075+
1076+ it ( "semver_equal returns false for invalid rule version" , ( ) => {
1077+ const rules : Rule [ ] = [
1078+ { operator : "semver_equal" , field : "version" , value : "not-semver" } ,
1079+ ] ;
1080+ expect (
1081+ isEligibleForStrategy ( rules , { __id : "yo" , version : "1.0.0" } )
1082+ ) . toBe ( false ) ;
1083+ } ) ;
1084+
1085+ it ( "semver_equal returns false when field is non-string" , ( ) => {
1086+ const rules : Rule [ ] = [
1087+ { operator : "semver_equal" , field : "version" , value : "1.0.0" } ,
1088+ ] ;
1089+ expect (
1090+ isEligibleForStrategy ( rules , { __id : "yo" , version : 100 } )
1091+ ) . toBe ( false ) ;
1092+ } ) ;
1093+
1094+ it ( "semver_equal returns false when field is missing" , ( ) => {
1095+ const rules : Rule [ ] = [
1096+ { operator : "semver_equal" , field : "version" , value : "1.0.0" } ,
1097+ ] ;
1098+ expect ( isEligibleForStrategy ( rules , { __id : "yo" } ) ) . toBe ( false ) ;
1099+ } ) ;
1100+
1101+ // ── semver_greater_than ─────────────────────────────────────────────
1102+
1103+ it . each ( [
1104+ [ "2.0.0" , "1.0.0" , true ] ,
1105+ [ "1.0.0" , "2.0.0" , false ] ,
1106+ [ "1.0.0" , "1.0.0" , false ] , // equal, not greater
1107+ [ "1.1.0" , "1.0.0" , true ] , // minor bump
1108+ [ "1.0.1" , "1.0.0" , true ] , // patch bump
1109+ [ "1.0.0" , "1.0.0-alpha" , true ] , // release > prerelease
1110+ [ "1.0.0-beta" , "1.0.0-alpha" , true ] , // prerelease ordering
1111+ [ "1.0.0-alpha" , "1.0.0-beta" , false ] ,
1112+ [ "1.0.0-alpha.2" , "1.0.0-alpha.1" , true ] ,
1113+ [ "10.0.0" , "9.99.99" , true ] ,
1114+ [ "1.0.0+build.1" , "1.0.0+build.2" , false ] , // build metadata ignored, versions are equal
1115+ [ "0.0.2" , "0.0.1" , true ] ,
1116+ ] ) (
1117+ "when field is '%s' and rule value is '%s', then it should return '%s' for operator semver_greater_than" ,
1118+ ( userValue , ruleValue , expected ) => {
1119+ const rules : Rule [ ] = [
1120+ {
1121+ operator : "semver_greater_than" ,
1122+ field : "version" ,
1123+ value : ruleValue ,
1124+ } ,
1125+ ] ;
1126+
1127+ expect (
1128+ isEligibleForStrategy ( rules , { __id : "yo" , version : userValue } )
1129+ ) . toBe ( expected ) ;
1130+ }
1131+ ) ;
1132+
1133+ it ( "semver_greater_than returns false for invalid user version" , ( ) => {
1134+ const rules : Rule [ ] = [
1135+ { operator : "semver_greater_than" , field : "version" , value : "1.0.0" } ,
1136+ ] ;
1137+ expect (
1138+ isEligibleForStrategy ( rules , { __id : "yo" , version : "abc" } )
1139+ ) . toBe ( false ) ;
1140+ } ) ;
1141+
1142+ it ( "semver_greater_than returns false for invalid rule version" , ( ) => {
1143+ const rules : Rule [ ] = [
1144+ { operator : "semver_greater_than" , field : "version" , value : "xyz" } ,
1145+ ] ;
1146+ expect (
1147+ isEligibleForStrategy ( rules , { __id : "yo" , version : "2.0.0" } )
1148+ ) . toBe ( false ) ;
1149+ } ) ;
1150+
1151+ it ( "semver_greater_than returns false when field is non-string" , ( ) => {
1152+ const rules : Rule [ ] = [
1153+ { operator : "semver_greater_than" , field : "version" , value : "1.0.0" } ,
1154+ ] ;
1155+ expect (
1156+ isEligibleForStrategy ( rules , { __id : "yo" , version : 200 } )
1157+ ) . toBe ( false ) ;
1158+ } ) ;
1159+
1160+ it ( "semver_greater_than returns false when field is missing" , ( ) => {
1161+ const rules : Rule [ ] = [
1162+ { operator : "semver_greater_than" , field : "version" , value : "1.0.0" } ,
1163+ ] ;
1164+ expect ( isEligibleForStrategy ( rules , { __id : "yo" } ) ) . toBe ( false ) ;
1165+ } ) ;
1166+
1167+ // ── semver_less_than ────────────────────────────────────────────────
1168+
1169+ it . each ( [
1170+ [ "1.0.0" , "2.0.0" , true ] ,
1171+ [ "2.0.0" , "1.0.0" , false ] ,
1172+ [ "1.0.0" , "1.0.0" , false ] , // equal, not less
1173+ [ "1.0.0" , "1.1.0" , true ] , // minor bump
1174+ [ "1.0.0" , "1.0.1" , true ] , // patch bump
1175+ [ "1.0.0-alpha" , "1.0.0" , true ] , // prerelease < release
1176+ [ "1.0.0-alpha" , "1.0.0-beta" , true ] , // prerelease ordering
1177+ [ "1.0.0-beta" , "1.0.0-alpha" , false ] ,
1178+ [ "1.0.0-alpha.1" , "1.0.0-alpha.2" , true ] ,
1179+ [ "9.99.99" , "10.0.0" , true ] ,
1180+ [ "1.0.0+build.1" , "1.0.0+build.2" , false ] , // build metadata ignored, versions are equal
1181+ [ "0.0.1" , "0.0.2" , true ] ,
1182+ ] ) (
1183+ "when field is '%s' and rule value is '%s', then it should return '%s' for operator semver_less_than" ,
1184+ ( userValue , ruleValue , expected ) => {
1185+ const rules : Rule [ ] = [
1186+ {
1187+ operator : "semver_less_than" ,
1188+ field : "version" ,
1189+ value : ruleValue ,
1190+ } ,
1191+ ] ;
1192+
1193+ expect (
1194+ isEligibleForStrategy ( rules , { __id : "yo" , version : userValue } )
1195+ ) . toBe ( expected ) ;
1196+ }
1197+ ) ;
1198+
1199+ it ( "semver_less_than returns false for invalid user version" , ( ) => {
1200+ const rules : Rule [ ] = [
1201+ { operator : "semver_less_than" , field : "version" , value : "2.0.0" } ,
1202+ ] ;
1203+ expect (
1204+ isEligibleForStrategy ( rules , { __id : "yo" , version : "abc" } )
1205+ ) . toBe ( false ) ;
1206+ } ) ;
1207+
1208+ it ( "semver_less_than returns false for invalid rule version" , ( ) => {
1209+ const rules : Rule [ ] = [
1210+ { operator : "semver_less_than" , field : "version" , value : "xyz" } ,
1211+ ] ;
1212+ expect (
1213+ isEligibleForStrategy ( rules , { __id : "yo" , version : "1.0.0" } )
1214+ ) . toBe ( false ) ;
1215+ } ) ;
1216+
1217+ it ( "semver_less_than returns false when field is non-string" , ( ) => {
1218+ const rules : Rule [ ] = [
1219+ { operator : "semver_less_than" , field : "version" , value : "2.0.0" } ,
1220+ ] ;
1221+ expect (
1222+ isEligibleForStrategy ( rules , { __id : "yo" , version : 100 } )
1223+ ) . toBe ( false ) ;
1224+ } ) ;
1225+
1226+ it ( "semver_less_than returns false when field is missing" , ( ) => {
1227+ const rules : Rule [ ] = [
1228+ { operator : "semver_less_than" , field : "version" , value : "2.0.0" } ,
1229+ ] ;
1230+ expect ( isEligibleForStrategy ( rules , { __id : "yo" } ) ) . toBe ( false ) ;
1231+ } ) ;
1232+
10341233 describe ( "greater_than / less_than edge cases" , ( ) => {
10351234 it ( "greater_than with Infinity" , ( ) => {
10361235 const rules : Rule [ ] = [
0 commit comments