1
16
17 package features
18
19 import (
20 "fmt"
21 "testing"
22
23 "github.com/stretchr/testify/require"
24 )
25
26 func TestEnvVarFeatureGates(t *testing.T) {
27 defaultTestFeatures := map[Feature]FeatureSpec{
28 "TestAlpha": {
29 Default: false,
30 LockToDefault: false,
31 PreRelease: "Alpha",
32 },
33 "TestBeta": {
34 Default: true,
35 LockToDefault: false,
36 PreRelease: "Beta",
37 },
38 }
39 expectedDefaultFeaturesState := map[Feature]bool{"TestAlpha": false, "TestBeta": true}
40
41 copyExpectedStateMap := func(toCopy map[Feature]bool) map[Feature]bool {
42 m := map[Feature]bool{}
43 for k, v := range toCopy {
44 m[k] = v
45 }
46 return m
47 }
48
49 scenarios := []struct {
50 name string
51 features map[Feature]FeatureSpec
52 envVariables map[string]string
53 expectedFeaturesState map[Feature]bool
54 expectedInternalEnabledFeatureState map[Feature]bool
55 }{
56 {
57 name: "can add empty features",
58 },
59 {
60 name: "no env var, features get Defaults assigned",
61 features: defaultTestFeatures,
62 expectedFeaturesState: expectedDefaultFeaturesState,
63 },
64 {
65 name: "incorrect env var, feature gets Default assigned",
66 features: defaultTestFeatures,
67 envVariables: map[string]string{"TestAlpha": "true"},
68 expectedFeaturesState: expectedDefaultFeaturesState,
69 },
70 {
71 name: "correct env var changes the feature gets state",
72 features: defaultTestFeatures,
73 envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": "true"},
74 expectedFeaturesState: func() map[Feature]bool {
75 expectedDefaultFeaturesStateCopy := copyExpectedStateMap(expectedDefaultFeaturesState)
76 expectedDefaultFeaturesStateCopy["TestAlpha"] = true
77 return expectedDefaultFeaturesStateCopy
78 }(),
79 expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
80 },
81 {
82 name: "incorrect env var value gets ignored",
83 features: defaultTestFeatures,
84 envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": "TrueFalse"},
85 expectedFeaturesState: expectedDefaultFeaturesState,
86 },
87 {
88 name: "empty env var value gets ignored",
89 features: defaultTestFeatures,
90 envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": ""},
91 expectedFeaturesState: expectedDefaultFeaturesState,
92 },
93 {
94 name: "a feature LockToDefault wins",
95 features: map[Feature]FeatureSpec{
96 "TestAlpha": {
97 Default: true,
98 LockToDefault: true,
99 PreRelease: "Alpha",
100 },
101 },
102 envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": "False"},
103 expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
104 },
105 {
106 name: "setting a feature to LockToDefault changes the internal state",
107 features: map[Feature]FeatureSpec{
108 "TestAlpha": {
109 Default: true,
110 LockToDefault: true,
111 PreRelease: "Alpha",
112 },
113 },
114 envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": "True"},
115 expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
116 expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
117 },
118 }
119 for _, scenario := range scenarios {
120 t.Run(scenario.name, func(t *testing.T) {
121 for k, v := range scenario.envVariables {
122 t.Setenv(k, v)
123 }
124 target := newEnvVarFeatureGates(scenario.features)
125
126 for expectedFeature, expectedValue := range scenario.expectedFeaturesState {
127 actualValue := target.Enabled(expectedFeature)
128 require.Equal(t, actualValue, expectedValue, "expected feature=%v, to be=%v, not=%v", expectedFeature, expectedValue, actualValue)
129 }
130
131 enabledInternalMap := target.enabled.Load().(map[Feature]bool)
132 require.Len(t, enabledInternalMap, len(scenario.expectedInternalEnabledFeatureState))
133
134 for expectedFeature, expectedInternalPresence := range scenario.expectedInternalEnabledFeatureState {
135 featureInternalValue, featureSet := enabledInternalMap[expectedFeature]
136 require.Equal(t, expectedInternalPresence, featureSet, "feature %v present = %v, expected = %v", expectedFeature, featureSet, expectedInternalPresence)
137
138 expectedFeatureInternalValue := scenario.expectedFeaturesState[expectedFeature]
139 require.Equal(t, expectedFeatureInternalValue, featureInternalValue)
140 }
141 })
142 }
143 }
144
145 func TestEnvVarFeatureGatesEnabledPanic(t *testing.T) {
146 target := newEnvVarFeatureGates(nil)
147 require.PanicsWithError(t, fmt.Errorf("feature %q is not registered in FeatureGates %q", "UnknownFeature", target.callSiteName).Error(), func() { target.Enabled("UnknownFeature") })
148 }
149
150 func TestHasAlreadyReadEnvVar(t *testing.T) {
151 target := newEnvVarFeatureGates(nil)
152 require.False(t, target.hasAlreadyReadEnvVar())
153
154 _ = target.getEnabledMapFromEnvVar()
155 require.True(t, target.hasAlreadyReadEnvVar())
156 }
157
View as plain text