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  }