github.com/diamondo25/ghz@v0.12.0/config/config_test.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "math" 7 "os" 8 "runtime" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 const expected = `{"proto":"asdf","protoset":"","call":"","cert":"","cName":"","n":0,"c":0,"q":0,"t":0,"D":"","M":"","o":"","O":"oval","host":"","T":0,"L":0,"cpus":0,"z":"4h30m0s","x":""}` 16 17 func TestConfig_MarshalJSON(t *testing.T) { 18 z, _ := time.ParseDuration("4h30m") 19 c := Config{Proto: "asdf", Z: z, Format: "oval"} 20 cJSON, err := json.Marshal(&c) 21 assert.NoError(t, err) 22 assert.Equal(t, expected, string(cJSON)) 23 } 24 25 func TestConfig_UnmarshalJSON(t *testing.T) { 26 t.Run("duration", func(t *testing.T) { 27 c := Config{} 28 err := json.Unmarshal([]byte(expected), &c) 29 z, _ := time.ParseDuration("4h30m") 30 ec := Config{Proto: "asdf", Z: z, Format: "oval"} 31 32 assert.NoError(t, err) 33 assert.Equal(t, ec, c) 34 assert.Equal(t, ec.Z.String(), c.Z.String()) 35 }) 36 37 t.Run("data not present", func(t *testing.T) { 38 jsonStr := `{"proto":"protofile", "call":"someCall"}` 39 c := Config{} 40 err := json.Unmarshal([]byte(jsonStr), &c) 41 ec := Config{Proto: "protofile", Call: "someCall"} 42 43 assert.NoError(t, err) 44 assert.Equal(t, ec, c) 45 assert.Nil(t, c.Data) 46 }) 47 48 t.Run("data empty string should fail", func(t *testing.T) { 49 jsonStr := `{"proto":"pf", "call":"sc", "d":""}` 50 c := Config{} 51 err := json.Unmarshal([]byte(jsonStr), &c) 52 53 assert.Error(t, err) 54 assert.Equal(t, "Unsupported type for Data", err.Error()) 55 }) 56 57 t.Run("data string should fail", func(t *testing.T) { 58 jsonStr := `{"proto":"pf", "call":"sc", "d":"bob"}` 59 c := Config{} 60 err := json.Unmarshal([]byte(jsonStr), &c) 61 62 assert.Error(t, err) 63 assert.Equal(t, "Unsupported type for Data", err.Error()) 64 }) 65 66 t.Run("data array of strings should fail", func(t *testing.T) { 67 jsonStr := `{"proto":"pf", "call":"sc", "d":["bob", "kate"]}` 68 c := Config{} 69 err := json.Unmarshal([]byte(jsonStr), &c) 70 71 assert.Error(t, err) 72 assert.Equal(t, "Data array contains unsupported type", err.Error()) 73 }) 74 75 t.Run("data empty object", func(t *testing.T) { 76 jsonStr := `{"proto":"pf", "call":"sc", "d":{}}` 77 c := Config{} 78 err := json.Unmarshal([]byte(jsonStr), &c) 79 80 assert.NoError(t, err) 81 assert.NotNil(t, c.Data) 82 assert.Empty(t, c.Data) 83 }) 84 85 t.Run("data empty array", func(t *testing.T) { 86 jsonStr := `{"proto":"pf", "call":"sc", "d":[]}` 87 c := Config{} 88 err := json.Unmarshal([]byte(jsonStr), &c) 89 90 assert.Error(t, err) 91 assert.Equal(t, "Data array must not be empty", err.Error()) 92 }) 93 94 t.Run("data valid object", func(t *testing.T) { 95 jsonStr := `{"proto":"pf", "call":"sc", "d":{"name":"bob"}}` 96 c := Config{} 97 err := json.Unmarshal([]byte(jsonStr), &c) 98 99 assert.NoError(t, err) 100 101 expected := make(map[string]interface{}) 102 expected["name"] = "bob" 103 assert.Equal(t, expected, c.Data) 104 }) 105 106 t.Run("data valid array of objects", func(t *testing.T) { 107 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"}, {"name":"kate"}]}` 108 c := Config{} 109 err := json.Unmarshal([]byte(jsonStr), &c) 110 111 assert.NoError(t, err) 112 assert.NotNil(t, c.Data) 113 assert.NotEmpty(t, c.Data) 114 assert.Len(t, c.Data, 2) 115 116 expected1 := make(map[string]interface{}) 117 expected2 := make(map[string]interface{}) 118 expected1["name"] = "bob" 119 expected2["name"] = "kate" 120 121 actual, ok := c.Data.([]interface{}) 122 assert.True(t, ok) 123 assert.NotNil(t, actual) 124 125 assert.Equal(t, expected1, actual[0]) 126 assert.Equal(t, expected2, actual[1]) 127 }) 128 129 t.Run("data valid array of empty objects", func(t *testing.T) { 130 jsonStr := `{"proto":"pf", "call":"sc", "d":[{},{},{}]}` 131 c := Config{} 132 err := json.Unmarshal([]byte(jsonStr), &c) 133 134 assert.NoError(t, err) 135 assert.NotNil(t, c.Data) 136 assert.NotEmpty(t, c.Data) 137 assert.Len(t, c.Data, 3) 138 139 expected := make(map[string]interface{}) 140 141 actual, ok := c.Data.([]interface{}) 142 assert.True(t, ok) 143 assert.NotNil(t, actual) 144 145 assert.Equal(t, expected, actual[0]) 146 assert.Equal(t, expected, actual[1]) 147 assert.Equal(t, expected, actual[2]) 148 }) 149 150 t.Run("data valid array of mixed objects", func(t *testing.T) { 151 jsonStr := `{"proto":"pf", "call":"sc", "d":[{},{"name":"bob"},{}]}` 152 c := Config{} 153 err := json.Unmarshal([]byte(jsonStr), &c) 154 155 assert.NoError(t, err) 156 assert.NotNil(t, c.Data) 157 assert.NotEmpty(t, c.Data) 158 assert.Len(t, c.Data, 3) 159 160 expected := make(map[string]interface{}) 161 expected2 := make(map[string]interface{}) 162 expected2["name"] = "bob" 163 164 actual, ok := c.Data.([]interface{}) 165 assert.True(t, ok) 166 assert.NotNil(t, actual) 167 168 assert.Equal(t, expected, actual[0]) 169 assert.Equal(t, expected2, actual[1]) 170 assert.Equal(t, expected, actual[2]) 171 }) 172 173 t.Run("data mixed with invalid should fail", func(t *testing.T) { 174 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"},"kate",{"name":"kate"}]}` 175 c := Config{} 176 err := json.Unmarshal([]byte(jsonStr), &c) 177 178 assert.Error(t, err) 179 assert.Equal(t, "Data array contains unsupported type", err.Error()) 180 }) 181 182 t.Run("invalid metadata array", func(t *testing.T) { 183 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"},{"name":"kate"}],"m":["requestId","1234"]}` 184 c := Config{} 185 err := json.Unmarshal([]byte(jsonStr), &c) 186 187 assert.Error(t, err) 188 }) 189 190 t.Run("invalid metadata shape", func(t *testing.T) { 191 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"},{"name":"kate"}],"m":{"requestId":{"n":"1234"}}}` 192 c := Config{} 193 err := json.Unmarshal([]byte(jsonStr), &c) 194 195 assert.Error(t, err) 196 }) 197 198 t.Run("invalid metadata type", func(t *testing.T) { 199 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"},{"name":"kate"}],"m":{"requestId":1234}}` 200 c := Config{} 201 err := json.Unmarshal([]byte(jsonStr), &c) 202 203 assert.Error(t, err) 204 }) 205 206 t.Run("metadata", func(t *testing.T) { 207 jsonStr := `{"proto":"pf", "call":"sc", "d":[{"name":"bob"},{"name":"kate"}],"m":{"requestId":"1234"}}` 208 c := Config{} 209 err := json.Unmarshal([]byte(jsonStr), &c) 210 211 assert.NoError(t, err) 212 assert.NotNil(t, c.Data) 213 assert.NotEmpty(t, c.Data) 214 assert.Len(t, c.Data, 2) 215 216 expected1 := make(map[string]interface{}) 217 expected2 := make(map[string]interface{}) 218 expected1["name"] = "bob" 219 expected2["name"] = "kate" 220 221 actual, ok := c.Data.([]interface{}) 222 assert.True(t, ok) 223 assert.NotNil(t, actual) 224 225 assert.Equal(t, expected1, actual[0]) 226 assert.Equal(t, expected2, actual[1]) 227 228 assert.Equal(t, "pf", c.Proto) 229 assert.Equal(t, "sc", c.Call) 230 231 expectedMD := make(map[string]string, 1) 232 expectedMD["requestId"] = "1234" 233 assert.Equal(t, expectedMD, *c.Metadata) 234 }) 235 } 236 237 func TestConfig_Default(t *testing.T) { 238 c := &Config{} 239 c.Default() 240 241 assert.Equal(t, c.N, 200) 242 assert.Equal(t, c.C, 50) 243 assert.Equal(t, c.CPUs, runtime.GOMAXPROCS(-1)) 244 } 245 246 func TestConfig_ReadConfig(t *testing.T) { 247 os.Chdir("../testdata/") 248 249 t.Run("ghz.json", func(t *testing.T) { 250 c, err := ReadConfig("../testdata/ghz.json") 251 252 assert.NoError(t, err) 253 assert.NotNil(t, c) 254 255 if c != nil { 256 data := make(map[string]interface{}) 257 data["name"] = "mydata" 258 259 ec := Config{ 260 Proto: "my.proto", 261 Call: "mycall", 262 Data: data, 263 Cert: "mycert", 264 CName: "localhost", 265 N: 200, 266 C: 50, 267 QPS: 0, 268 Z: 0, 269 Timeout: 20, 270 DataPath: "", 271 MetadataPath: "", 272 Format: "", 273 Output: "", 274 Host: "", 275 DialTimeout: 10, 276 KeepaliveTime: 0, 277 CPUs: runtime.GOMAXPROCS(-1), 278 ImportPaths: []string{"/path/to/protos", "."}} 279 280 assert.Equal(t, ec, *c) 281 assert.Equal(t, ec.Data, c.Data) 282 } 283 }) 284 285 t.Run("cfgpath.json", func(t *testing.T) { 286 c, err := ReadConfig("../testdata/cfgpath.json") 287 288 assert.NoError(t, err) 289 assert.NotNil(t, c) 290 291 if c != nil { 292 data := make(map[string]interface{}) 293 data["name"] = "mydata" 294 295 metaData := make(map[string]string) 296 metaData["requestId"] = "12345" 297 298 ec := Config{ 299 Proto: "my.proto", 300 Call: "mycall", 301 Data: data, 302 Cert: "mycert", 303 CName: "localhost", 304 N: 200, 305 C: 50, 306 QPS: 0, 307 Z: 0, 308 Timeout: 20, 309 Metadata: &metaData, 310 DataPath: "./data.json", 311 MetadataPath: "./metadata.json", 312 Format: "", 313 Output: "", 314 Host: "", 315 DialTimeout: 10, 316 KeepaliveTime: 0, 317 CPUs: runtime.GOMAXPROCS(-1), 318 ImportPaths: []string{"/path/to/protos", "."}} 319 320 assert.Equal(t, ec, *c) 321 assert.Equal(t, ec.Data, c.Data) 322 assert.Equal(t, ec.Metadata, c.Metadata) 323 324 actualMD := *c.Metadata 325 val := actualMD["requestId"] 326 assert.Equal(t, "12345", val) 327 } 328 }) 329 } 330 331 func TestConfig_Validate(t *testing.T) { 332 t.Run("missing proto", func(t *testing.T) { 333 c := &Config{} 334 err := c.Validate() 335 assert.Equal(t, "Proto or Protoset required", err.Error()) 336 }) 337 338 t.Run("invalid proto", func(t *testing.T) { 339 c := &Config{Proto: "asdf"} 340 err := c.Validate() 341 assert.Equal(t, "proto: must have .proto extension", err.Error()) 342 }) 343 344 t.Run("missing call", func(t *testing.T) { 345 c := &Config{Proto: "asdf.proto"} 346 err := c.Validate() 347 assert.Equal(t, "call: is required", err.Error()) 348 }) 349 350 t.Run("N < 0", func(t *testing.T) { 351 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", N: -1} 352 err := c.Validate() 353 assert.Equal(t, "n: must be at least 0", err.Error()) 354 }) 355 356 t.Run("C < 0", func(t *testing.T) { 357 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", C: -1} 358 err := c.Validate() 359 assert.Equal(t, "c: must be at least 0", err.Error()) 360 }) 361 362 t.Run("QPS < 0", func(t *testing.T) { 363 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", QPS: -1} 364 err := c.Validate() 365 assert.Equal(t, "q: must be at least 0", err.Error()) 366 }) 367 368 t.Run("T < 0", func(t *testing.T) { 369 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", Timeout: -1} 370 err := c.Validate() 371 assert.Equal(t, "t: must be at least 0", err.Error()) 372 }) 373 374 t.Run("CPUs < 0", func(t *testing.T) { 375 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", CPUs: -1} 376 err := c.Validate() 377 assert.Equal(t, "cpus: must be at least 0", err.Error()) 378 }) 379 380 t.Run("missing data", func(t *testing.T) { 381 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert"} 382 err := c.Validate() 383 assert.Equal(t, "data: is required", err.Error()) 384 }) 385 386 t.Run("DataPath", func(t *testing.T) { 387 c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", DataPath: "asdf"} 388 err := c.Validate() 389 assert.NoError(t, err) 390 }) 391 } 392 393 func TestConfig_initData(t *testing.T) { 394 t.Run("when empty", func(t *testing.T) { 395 c := &Config{} 396 err := c.initData() 397 assert.Equal(t, "No data specified", err.Error()) 398 }) 399 400 t.Run("with map data", func(t *testing.T) { 401 data := make(map[string]interface{}) 402 data["name"] = "mydata" 403 c := &Config{Data: &data} 404 err := c.initData() 405 assert.NoError(t, err) 406 assert.Equal(t, c.Data, &data) 407 }) 408 409 t.Run("with file specified", func(t *testing.T) { 410 data := make(map[string]interface{}) 411 dat, err := ioutil.ReadFile("../testdata/data.json") 412 assert.NoError(t, err) 413 err = json.Unmarshal([]byte(dat), &data) 414 assert.NoError(t, err) 415 416 c := &Config{DataPath: "../testdata/data.json"} 417 err = c.initData() 418 assert.NoError(t, err) 419 assert.Equal(t, c.Data, data) 420 }) 421 } 422 423 func TestConfig_initDurations(t *testing.T) { 424 t.Run("with Z specified should set N to max int", func(t *testing.T) { 425 dur, _ := time.ParseDuration("4h30m") 426 c := &Config{N: 500, Z: dur} 427 c.initDurations() 428 assert.Equal(t, c.N, math.MaxInt32) 429 }) 430 431 t.Run("with X specified should set Z to X and keep N", func(t *testing.T) { 432 dur, _ := time.ParseDuration("4h30m") 433 c := &Config{N: 500, X: dur} 434 c.initDurations() 435 assert.Equal(t, c.N, 500) 436 assert.Equal(t, c.X, dur) 437 assert.Equal(t, c.Z, dur) 438 }) 439 440 t.Run("with X and Z specified should set Z to X and keep N", func(t *testing.T) { 441 dur, _ := time.ParseDuration("4m") 442 dur2, _ := time.ParseDuration("5m") 443 c := &Config{N: 500, X: dur, Z: dur2} 444 c.initDurations() 445 assert.Equal(t, c.N, 500) 446 assert.Equal(t, c.X, dur) 447 assert.Equal(t, c.Z, dur) 448 }) 449 }