github.com/safing/portbase@v0.19.5/config/get_test.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "testing" 7 8 "github.com/safing/portbase/log" 9 ) 10 11 func parseAndReplaceConfig(jsonData string) error { 12 m, err := JSONToMap([]byte(jsonData)) 13 if err != nil { 14 return err 15 } 16 17 validationErrors, _ := ReplaceConfig(m) 18 if len(validationErrors) > 0 { 19 return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0]) 20 } 21 return nil 22 } 23 24 func parseAndReplaceDefaultConfig(jsonData string) error { 25 m, err := JSONToMap([]byte(jsonData)) 26 if err != nil { 27 return err 28 } 29 30 validationErrors, _ := ReplaceDefaultConfig(m) 31 if len(validationErrors) > 0 { 32 return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0]) 33 } 34 return nil 35 } 36 37 func quickRegister(t *testing.T, key string, optType OptionType, defaultValue interface{}) { 38 t.Helper() 39 40 err := Register(&Option{ 41 Name: key, 42 Key: key, 43 Description: "test config", 44 ReleaseLevel: ReleaseLevelStable, 45 ExpertiseLevel: ExpertiseLevelUser, 46 OptType: optType, 47 DefaultValue: defaultValue, 48 }) 49 if err != nil { 50 t.Fatal(err) 51 } 52 } 53 54 func TestGet(t *testing.T) { //nolint:paralleltest 55 // reset 56 options = make(map[string]*Option) 57 58 err := log.Start() 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 quickRegister(t, "monkey", OptTypeString, "c") 64 quickRegister(t, "zebras/zebra", OptTypeStringArray, []string{"a", "b"}) 65 quickRegister(t, "elephant", OptTypeInt, -1) 66 quickRegister(t, "hot", OptTypeBool, false) 67 quickRegister(t, "cold", OptTypeBool, true) 68 69 err = parseAndReplaceConfig(` 70 { 71 "monkey": "a", 72 "zebras": { 73 "zebra": ["black", "white"] 74 }, 75 "elephant": 2, 76 "hot": true, 77 "cold": false 78 } 79 `) 80 if err != nil { 81 t.Fatal(err) 82 } 83 84 err = parseAndReplaceDefaultConfig(` 85 { 86 "monkey": "b", 87 "snake": "0", 88 "elephant": 0 89 } 90 `) 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 monkey := GetAsString("monkey", "none") 96 if monkey() != "a" { 97 t.Errorf("monkey should be a, is %s", monkey()) 98 } 99 100 zebra := GetAsStringArray("zebras/zebra", []string{}) 101 if len(zebra()) != 2 || zebra()[0] != "black" || zebra()[1] != "white" { 102 t.Errorf("zebra should be [\"black\", \"white\"], is %v", zebra()) 103 } 104 105 elephant := GetAsInt("elephant", -1) 106 if elephant() != 2 { 107 t.Errorf("elephant should be 2, is %d", elephant()) 108 } 109 110 hot := GetAsBool("hot", false) 111 if !hot() { 112 t.Errorf("hot should be true, is %v", hot()) 113 } 114 115 cold := GetAsBool("cold", true) 116 if cold() { 117 t.Errorf("cold should be false, is %v", cold()) 118 } 119 120 err = parseAndReplaceConfig(` 121 { 122 "monkey": "3" 123 } 124 `) 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 if monkey() != "3" { 130 t.Errorf("monkey should be 0, is %s", monkey()) 131 } 132 133 if elephant() != 0 { 134 t.Errorf("elephant should be 0, is %d", elephant()) 135 } 136 137 zebra() 138 hot() 139 140 // concurrent 141 GetAsString("monkey", "none")() 142 GetAsStringArray("zebras/zebra", []string{})() 143 GetAsInt("elephant", -1)() 144 GetAsBool("hot", false)() 145 146 // perspective 147 148 // load data 149 pLoaded := make(map[string]interface{}) 150 err = json.Unmarshal([]byte(`{ 151 "monkey": "a", 152 "zebras": { 153 "zebra": ["black", "white"] 154 }, 155 "elephant": 2, 156 "hot": true, 157 "cold": false 158 }`), &pLoaded) 159 if err != nil { 160 t.Fatal(err) 161 } 162 163 // create 164 p, err := NewPerspective(pLoaded) 165 if err != nil { 166 t.Fatal(err) 167 } 168 169 monkeyVal, ok := p.GetAsString("monkey") 170 if !ok || monkeyVal != "a" { 171 t.Errorf("[perspective] monkey should be a, is %+v", monkeyVal) 172 } 173 174 zebraVal, ok := p.GetAsStringArray("zebras/zebra") 175 if !ok || len(zebraVal) != 2 || zebraVal[0] != "black" || zebraVal[1] != "white" { 176 t.Errorf("[perspective] zebra should be [\"black\", \"white\"], is %+v", zebraVal) 177 } 178 179 elephantVal, ok := p.GetAsInt("elephant") 180 if !ok || elephantVal != 2 { 181 t.Errorf("[perspective] elephant should be 2, is %+v", elephantVal) 182 } 183 184 hotVal, ok := p.GetAsBool("hot") 185 if !ok || !hotVal { 186 t.Errorf("[perspective] hot should be true, is %+v", hotVal) 187 } 188 189 coldVal, ok := p.GetAsBool("cold") 190 if !ok || coldVal { 191 t.Errorf("[perspective] cold should be false, is %+v", coldVal) 192 } 193 } 194 195 func TestReleaseLevel(t *testing.T) { //nolint:paralleltest 196 // reset 197 options = make(map[string]*Option) 198 registerReleaseLevelOption() 199 200 // setup 201 subsystemOption := &Option{ 202 Name: "test subsystem", 203 Key: "subsystem/test", 204 Description: "test config", 205 ReleaseLevel: ReleaseLevelStable, 206 ExpertiseLevel: ExpertiseLevelUser, 207 OptType: OptTypeBool, 208 DefaultValue: false, 209 } 210 err := Register(subsystemOption) 211 if err != nil { 212 t.Fatal(err) 213 } 214 err = SetConfigOption("subsystem/test", true) 215 if err != nil { 216 t.Fatal(err) 217 } 218 testSubsystem := GetAsBool("subsystem/test", false) 219 220 // test option level stable 221 subsystemOption.ReleaseLevel = ReleaseLevelStable 222 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) 223 if err != nil { 224 t.Fatal(err) 225 } 226 if !testSubsystem() { 227 t.Error("should be active") 228 } 229 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) 230 if err != nil { 231 t.Fatal(err) 232 } 233 if !testSubsystem() { 234 t.Error("should be active") 235 } 236 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) 237 if err != nil { 238 t.Fatal(err) 239 } 240 if !testSubsystem() { 241 t.Error("should be active") 242 } 243 244 // test option level beta 245 subsystemOption.ReleaseLevel = ReleaseLevelBeta 246 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) 247 if err != nil { 248 t.Fatal(err) 249 } 250 if testSubsystem() { 251 t.Errorf("should be inactive: opt=%d system=%d", subsystemOption.ReleaseLevel, getReleaseLevel()) 252 } 253 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) 254 if err != nil { 255 t.Fatal(err) 256 } 257 if !testSubsystem() { 258 t.Error("should be active") 259 } 260 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) 261 if err != nil { 262 t.Fatal(err) 263 } 264 if !testSubsystem() { 265 t.Error("should be active") 266 } 267 268 // test option level experimental 269 subsystemOption.ReleaseLevel = ReleaseLevelExperimental 270 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable) 271 if err != nil { 272 t.Fatal(err) 273 } 274 if testSubsystem() { 275 t.Error("should be inactive") 276 } 277 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta) 278 if err != nil { 279 t.Fatal(err) 280 } 281 if testSubsystem() { 282 t.Error("should be inactive") 283 } 284 err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental) 285 if err != nil { 286 t.Fatal(err) 287 } 288 if !testSubsystem() { 289 t.Error("should be active") 290 } 291 } 292 293 func BenchmarkGetAsStringCached(b *testing.B) { 294 // reset 295 options = make(map[string]*Option) 296 297 // Setup 298 err := parseAndReplaceConfig(`{ 299 "monkey": "banana" 300 }`) 301 if err != nil { 302 b.Fatal(err) 303 } 304 monkey := GetAsString("monkey", "no banana") 305 306 // Reset timer for precise results 307 b.ResetTimer() 308 309 // Start benchmark 310 for i := 0; i < b.N; i++ { 311 monkey() 312 } 313 } 314 315 func BenchmarkGetAsStringRefetch(b *testing.B) { 316 // Setup 317 err := parseAndReplaceConfig(`{ 318 "monkey": "banana" 319 }`) 320 if err != nil { 321 b.Fatal(err) 322 } 323 324 // Reset timer for precise results 325 b.ResetTimer() 326 327 // Start benchmark 328 for i := 0; i < b.N; i++ { 329 getValueCache("monkey", nil, OptTypeString) 330 } 331 } 332 333 func BenchmarkGetAsIntCached(b *testing.B) { 334 // Setup 335 err := parseAndReplaceConfig(`{ 336 "elephant": 1 337 }`) 338 if err != nil { 339 b.Fatal(err) 340 } 341 elephant := GetAsInt("elephant", -1) 342 343 // Reset timer for precise results 344 b.ResetTimer() 345 346 // Start benchmark 347 for i := 0; i < b.N; i++ { 348 elephant() 349 } 350 } 351 352 func BenchmarkGetAsIntRefetch(b *testing.B) { 353 // Setup 354 err := parseAndReplaceConfig(`{ 355 "elephant": 1 356 }`) 357 if err != nil { 358 b.Fatal(err) 359 } 360 361 // Reset timer for precise results 362 b.ResetTimer() 363 364 // Start benchmark 365 for i := 0; i < b.N; i++ { 366 getValueCache("elephant", nil, OptTypeInt) 367 } 368 }