github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/features/features_test.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package features 18 19 import ( 20 "reflect" 21 "testing" 22 ) 23 24 func TestFeatureGateIsEnabled(t *testing.T) { 25 testFG := make(featureFlag) 26 testFG["feature1"] = false 27 testFG["feature2"] = true 28 tests := map[string]struct { 29 fg featureFlag 30 feature Feature 31 want bool 32 }{ 33 "when feature gate is empty": { 34 fg: nil, 35 feature: "test", 36 want: false, 37 }, 38 "when feature gate does not have the feature": { 39 fg: testFG, 40 feature: "feature3", 41 want: false, 42 }, 43 "when feature gate has the feature and feature is disabled": { 44 fg: testFG, 45 feature: "feature1", 46 want: false, 47 }, 48 "when feature gate has the feature and feature is enabled": { 49 fg: testFG, 50 feature: "feature2", 51 want: true, 52 }, 53 } 54 for name, tt := range tests { 55 t.Run(name, func(t *testing.T) { 56 if got := tt.fg.IsEnabled(tt.feature); got != tt.want { 57 t.Errorf("IsEnabled() = %v, want %v", got, tt.want) 58 } 59 }) 60 } 61 } 62 63 func TestSetFeatureFlag(t *testing.T) { 64 F1 := Feature("FeatureGate1") 65 F2 := Feature("FeatureGate2") 66 F3 := Feature("FeatureGate3") 67 supportedFeatures = []Feature{ 68 F1, F2, F3, 69 } 70 type args struct { 71 features []string 72 } 73 tests := map[string]struct { 74 args args 75 want featureFlag 76 wantErr bool 77 }{ 78 "empty feature gate slice": { 79 args: args{ 80 features: nil, 81 }, 82 want: make(featureFlag), 83 wantErr: false, 84 }, 85 "a single feature is added": { 86 args: args{ 87 features: []string{"FeatureGate1"}, 88 }, 89 want: map[Feature]bool{ 90 F1: true, 91 }, 92 wantErr: false, 93 }, 94 "a single feature is set in disabled mode": { 95 args: args{ 96 features: []string{"FeatureGate1=false"}, 97 }, 98 want: map[Feature]bool{ 99 F1: false, 100 }, 101 wantErr: false, 102 }, 103 "feature that is not present in the supported feature": { 104 args: args{ 105 features: []string{"WrongFeatureGate"}, 106 }, 107 want: make(featureFlag), 108 wantErr: true, 109 }, 110 "multiple non-default features in enabled and disabled state": { 111 args: args{ 112 features: []string{"FeatureGate1", "FeatureGate2=false", "FeatureGate3=true"}, 113 }, 114 want: featureFlag{ 115 F1: true, 116 F2: false, 117 F3: true, 118 }, 119 wantErr: false, 120 }, 121 "wrong format in one feature gate": { 122 args: args{ 123 features: []string{"FeatureGate1", "FeatureGate2=true=true"}, 124 }, 125 want: featureFlag{ 126 F1: true, 127 }, 128 wantErr: true, 129 }, 130 } 131 for name, tt := range tests { 132 t.Run(name, func(t *testing.T) { 133 fg := make(featureFlag) 134 err := fg.SetFeatureFlag(tt.args.features) 135 if (err != nil) != tt.wantErr { 136 t.Errorf("SetFeatureFlag() error = %v, wantErr %v", err, tt.wantErr) 137 return 138 } 139 if !reflect.DeepEqual(fg, tt.want) && err == nil { 140 t.Errorf("SetFeatureFlag() got = %v, want %v", fg, tt.want) 141 } 142 }) 143 } 144 } 145 146 func TestBasicFeatureDependencies(t *testing.T) { 147 firstFeature := Feature("FeatureGate1") 148 secondFeature := Feature("FeatureGate2") 149 featureDependsOnFirst := Feature("FeatureGateNeeds1") 150 featureDependsOnSecond := Feature("FeatureGateNeeds2") 151 featureDependsOnBoth := Feature("FeatureGateNeeds1And2") 152 153 supportedFeatures = []Feature{ 154 firstFeature, 155 secondFeature, 156 featureDependsOnFirst, 157 featureDependsOnSecond, 158 featureDependsOnBoth, 159 } 160 161 featureDependencies = map[Feature][]Feature{ 162 featureDependsOnFirst: {firstFeature}, 163 featureDependsOnSecond: {secondFeature}, 164 featureDependsOnBoth: {firstFeature, secondFeature}, 165 } 166 167 type args struct { 168 features []string 169 } 170 171 tests := map[string]struct { 172 args args 173 want featureFlag 174 }{ 175 "unmet dependency should fail": { 176 args: args{ 177 features: []string{string(featureDependsOnFirst)}, 178 }, 179 want: map[Feature]bool{ 180 featureDependsOnFirst: false, 181 }, 182 }, 183 "met dependency should succeed": { 184 args: args{ 185 features: []string{string(firstFeature), string(featureDependsOnFirst)}, 186 }, 187 want: map[Feature]bool{ 188 firstFeature: true, 189 featureDependsOnFirst: true, 190 }, 191 }, 192 "one of two unmet dependency should fail": { 193 args: args{ 194 features: []string{string(firstFeature), string(featureDependsOnBoth)}, 195 }, 196 want: map[Feature]bool{ 197 firstFeature: true, 198 featureDependsOnBoth: false, 199 }, 200 }, 201 "all dependencies met should succeed": { 202 args: args{ 203 features: []string{ 204 string(firstFeature), 205 string(secondFeature), 206 string(featureDependsOnBoth)}, 207 }, 208 want: map[Feature]bool{ 209 firstFeature: true, 210 secondFeature: true, 211 featureDependsOnBoth: true, 212 }, 213 }, 214 "multiple features can have same dependencies": { 215 args: args{ 216 features: []string{ 217 string(firstFeature), 218 string(secondFeature), 219 string(featureDependsOnFirst), 220 string(featureDependsOnSecond), 221 string(featureDependsOnBoth)}, 222 }, 223 want: map[Feature]bool{ 224 firstFeature: true, 225 secondFeature: true, 226 featureDependsOnFirst: true, 227 featureDependsOnSecond: true, 228 featureDependsOnBoth: true, 229 }, 230 }, 231 } 232 233 for name, test := range tests { 234 t.Run(name, func(t *testing.T) { 235 flags := make(featureFlag) 236 err := flags.SetFeatureFlag(test.args.features) 237 if err != nil { 238 t.Errorf("SetFeatureFlag() error = %v", err) 239 return 240 } 241 if !reflect.DeepEqual(flags, test.want) { 242 t.Errorf("SetFeatureFlag() got = %v, want %v", flags, test.want) 243 } 244 }) 245 } 246 247 } 248 249 func TestNestedFeatureDependencies(t *testing.T) { 250 firstFeature := Feature("FeatureGate1") 251 secondFeature := Feature("FeatureGate2") 252 thirdFeatureNeedsBoth := Feature("FeatureGate3Needs1And2") 253 fourthFeatureNeedsThird := Feature("FeatureGate4Needs3") 254 255 supportedFeatures = []Feature{ 256 firstFeature, 257 secondFeature, 258 thirdFeatureNeedsBoth, 259 fourthFeatureNeedsThird, 260 } 261 262 featureDependencies = map[Feature][]Feature{ 263 thirdFeatureNeedsBoth: {firstFeature, secondFeature}, 264 fourthFeatureNeedsThird: {thirdFeatureNeedsBoth}, 265 } 266 267 type args struct { 268 init []string 269 action []string 270 } 271 272 type want struct { 273 initResults featureFlag 274 actionResults featureFlag 275 } 276 277 tests := map[string]struct { 278 args args 279 want want 280 }{ 281 "satisfied nested features should succeed": { 282 args: args{ 283 init: []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth)}, 284 action: []string{string(fourthFeatureNeedsThird)}, 285 }, 286 want: want{ 287 initResults: map[Feature]bool{ 288 firstFeature: true, 289 secondFeature: true, 290 thirdFeatureNeedsBoth: true, 291 }, 292 actionResults: map[Feature]bool{ 293 firstFeature: true, 294 secondFeature: true, 295 thirdFeatureNeedsBoth: true, 296 fourthFeatureNeedsThird: true, 297 }, 298 }, 299 }, 300 "unsatisfied nested dependency should fail": { 301 args: args{ 302 init: []string{string(firstFeature)}, 303 action: []string{string(thirdFeatureNeedsBoth), string(fourthFeatureNeedsThird)}, 304 }, 305 want: want{ 306 initResults: map[Feature]bool{ 307 firstFeature: true, 308 }, 309 actionResults: map[Feature]bool{ 310 firstFeature: true, 311 thirdFeatureNeedsBoth: false, 312 fourthFeatureNeedsThird: false, 313 }, 314 }, 315 }, 316 "turning off dependency should turn off dependant": { 317 args: args{ 318 init: []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth)}, 319 action: []string{string(firstFeature + "=false")}, 320 }, 321 want: want{ 322 initResults: map[Feature]bool{ 323 firstFeature: true, 324 secondFeature: true, 325 thirdFeatureNeedsBoth: true, 326 }, 327 actionResults: map[Feature]bool{ 328 firstFeature: false, 329 secondFeature: true, 330 thirdFeatureNeedsBoth: false, 331 }, 332 }, 333 }, 334 "turning off dependency should turn off nested dependants": { 335 args: args{ 336 init: []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth), string(fourthFeatureNeedsThird)}, 337 action: []string{string(firstFeature + "=false")}, 338 }, 339 want: want{ 340 initResults: map[Feature]bool{ 341 firstFeature: true, 342 secondFeature: true, 343 thirdFeatureNeedsBoth: true, 344 fourthFeatureNeedsThird: true, 345 }, 346 actionResults: map[Feature]bool{ 347 firstFeature: false, 348 secondFeature: true, 349 thirdFeatureNeedsBoth: false, 350 fourthFeatureNeedsThird: false, 351 }, 352 }, 353 }, 354 } 355 356 for name, test := range tests { 357 t.Run(name, func(t *testing.T) { 358 flags := make(featureFlag) 359 initErr := flags.SetFeatureFlag(test.args.init) 360 if initErr != nil { 361 t.Errorf("SetFeatureFlag() error initializing test = %v", initErr) 362 return 363 } 364 if !reflect.DeepEqual(flags, test.want.initResults) { 365 t.Errorf("SetFeatureFlag() initializing test got = %v, want %v", flags, test.want) 366 return 367 } 368 err := flags.SetFeatureFlag(test.args.action) 369 if err != nil { 370 t.Errorf("SetFeatureFlag() error = %v", err) 371 return 372 } 373 if !reflect.DeepEqual(flags, test.want.actionResults) { 374 t.Errorf("SetFeatureFlag() got = %v, want %v", flags, test.want) 375 } 376 }) 377 } 378 379 }