github.com/tristanang/ghz@v0.18.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":"","B":"","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  	t.Run("BinDataPath", func(t *testing.T) {
   393  		c := &Config{Proto: "asdf.proto", Call: "call", Cert: "cert", BinDataPath: "asdf"}
   394  		err := c.Validate()
   395  		assert.NoError(t, err)
   396  	})
   397  }
   398  
   399  func TestConfig_initData(t *testing.T) {
   400  	t.Run("when empty", func(t *testing.T) {
   401  		c := &Config{}
   402  		err := c.initData()
   403  		assert.Equal(t, "No data specified", err.Error())
   404  	})
   405  
   406  	t.Run("with map data", func(t *testing.T) {
   407  		data := make(map[string]interface{})
   408  		data["name"] = "mydata"
   409  		c := &Config{Data: &data}
   410  		err := c.initData()
   411  		assert.NoError(t, err)
   412  		assert.Equal(t, c.Data, &data)
   413  	})
   414  
   415  	t.Run("with file specified", func(t *testing.T) {
   416  		data := make(map[string]interface{})
   417  		dat, err := ioutil.ReadFile("../testdata/data.json")
   418  		assert.NoError(t, err)
   419  		err = json.Unmarshal([]byte(dat), &data)
   420  		assert.NoError(t, err)
   421  
   422  		c := &Config{DataPath: "../testdata/data.json"}
   423  		err = c.initData()
   424  		assert.NoError(t, err)
   425  		assert.Equal(t, c.Data, data)
   426  	})
   427  
   428  	t.Run("with bin data", func(t *testing.T) {
   429  		in, err := ioutil.ReadFile("../testdata/hello_request_data.bin")
   430  		assert.NoError(t, err)
   431  		c := &Config{BinData: in}
   432  		err = c.initData()
   433  		assert.NoError(t, err)
   434  		assert.Equal(t, c.BinData, in)
   435  	})
   436  
   437  	t.Run("with bin data file", func(t *testing.T) {
   438  		in, err := ioutil.ReadFile("../testdata/hello_request_data.bin")
   439  		assert.NoError(t, err)
   440  		c := &Config{BinDataPath: "../testdata/hello_request_data.bin"}
   441  		err = c.initData()
   442  		assert.NoError(t, err)
   443  		assert.Equal(t, c.BinData, in)
   444  	})
   445  }
   446  
   447  func TestConfig_initDurations(t *testing.T) {
   448  	t.Run("with Z specified should set N to max int", func(t *testing.T) {
   449  		dur, _ := time.ParseDuration("4h30m")
   450  		c := &Config{N: 500, Z: dur}
   451  		c.initDurations()
   452  		assert.Equal(t, c.N, math.MaxInt32)
   453  	})
   454  
   455  	t.Run("with X specified should set Z to X and keep N", func(t *testing.T) {
   456  		dur, _ := time.ParseDuration("4h30m")
   457  		c := &Config{N: 500, X: dur}
   458  		c.initDurations()
   459  		assert.Equal(t, c.N, 500)
   460  		assert.Equal(t, c.X, dur)
   461  		assert.Equal(t, c.Z, dur)
   462  	})
   463  
   464  	t.Run("with X and Z specified should set Z to X and keep N", func(t *testing.T) {
   465  		dur, _ := time.ParseDuration("4m")
   466  		dur2, _ := time.ParseDuration("5m")
   467  		c := &Config{N: 500, X: dur, Z: dur2}
   468  		c.initDurations()
   469  		assert.Equal(t, c.N, 500)
   470  		assert.Equal(t, c.X, dur)
   471  		assert.Equal(t, c.Z, dur)
   472  	})
   473  }