trpc.group/trpc-go/trpc-go@v1.0.3/config_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package trpc
    15  
    16  import (
    17  	"os"
    18  	"strconv"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  	yaml "gopkg.in/yaml.v3"
    26  
    27  	"trpc.group/trpc-go/trpc-go/errs"
    28  	"trpc.group/trpc-go/trpc-go/rpcz"
    29  )
    30  
    31  // TestLoadGlobalConfigNICError tests LoadGlobalConfig error.
    32  func TestLoadGlobalConfigNICError(t *testing.T) {
    33  	err := LoadGlobalConfig("testdata/trpc_go_error.yaml")
    34  	assert.NotNil(t, err)
    35  }
    36  
    37  // TestRepairServiceIPWithNicAdminError tests repairServiceIPWithNic for admin error.
    38  func TestRepairServiceIPWithNicAdminError(t *testing.T) {
    39  	conf := &Config{}
    40  	conf.Server.Admin.IP = ""
    41  	conf.Server.Admin.Nic = "ethNoneExist"
    42  	err := repairServiceIPWithNic(conf)
    43  	assert.Contains(t, err.Error(), "can't find admin IP by the NIC")
    44  
    45  }
    46  
    47  func TestParseEnv(t *testing.T) {
    48  	filePath := "./trpc_go.yaml"
    49  	content := `
    50  global:
    51    namespace: development
    52    env_name: ${test}
    53    container_name: ${container_name}
    54    local_ip: $local_ip
    55  server:
    56    app: ${}
    57    server: ${server
    58  client:
    59    timeout: ${client_timeout}  
    60  `
    61  	err := os.WriteFile(filePath, []byte(content), 0644)
    62  	if err != nil {
    63  		t.Errorf("write file err: %v", err)
    64  		return
    65  	}
    66  	defer func() {
    67  		_ = os.Remove(filePath)
    68  	}()
    69  
    70  	containerName := `这是容器名称,this is container name.`
    71  	_ = os.Setenv("container_name", containerName)
    72  	localIP := `127.1.2.3`
    73  	_ = os.Setenv("local_ip", localIP)
    74  	timeout := 456
    75  	_ = os.Setenv("client_timeout", strconv.FormatInt(int64(timeout), 10))
    76  
    77  	c, err := parseConfigFromFile(filePath)
    78  	if err != nil {
    79  		t.Errorf("parse err: %v", err)
    80  		return
    81  	}
    82  	assert.Equal(t, c.Global.Namespace, "development")
    83  	assert.Equal(t, c.Global.EnvName, "") // env name not set, should be replaced with empty value
    84  	assert.Equal(t, c.Global.ContainerName, containerName)
    85  	assert.Equal(t, c.Global.LocalIP, "$local_ip") // only ${var} instead of $var is valid
    86  	assert.Equal(t, c.Server.App, "")              // empty ${} should be deleted
    87  	assert.Equal(t, c.Server.Server, "${server")   // only ${var} is valid
    88  	assert.Equal(t, c.Client.Timeout, timeout)
    89  }
    90  
    91  func TestParseEnvSpecialChar(t *testing.T) {
    92  	filePath := "./trpc_go.yaml"
    93  	content := `
    94  global:
    95    namespace: development
    96    env_name: test
    97    container_name: ${container_name}
    98    local_ip: ${local_ip}
    99  `
   100  	err := os.WriteFile(filePath, []byte(content), 0644)
   101  	if err != nil {
   102  		t.Errorf("write file err: %v", err)
   103  		return
   104  	}
   105  	defer func() {
   106  		_ = os.Remove(filePath)
   107  	}()
   108  
   109  	containerName := `'这是"容器名称": this is container name. !@#$%^&*()_+-='`
   110  	_ = os.Setenv("container_name", containerName)
   111  	actualContainerName := strings.Trim(containerName, `'`) // single quotes
   112  	localIP := `"'127.1.2.3'"`
   113  	_ = os.Setenv("local_ip", localIP)
   114  	actualLocalIP := strings.Trim(localIP, `"`) // double quotes
   115  
   116  	c, err := parseConfigFromFile(filePath)
   117  	if err != nil {
   118  		t.Errorf("parse err: %v", err)
   119  		return
   120  	}
   121  	assert.Equal(t, c.Global.Namespace, "development")
   122  	assert.Equal(t, c.Global.EnvName, "test")
   123  	assert.Equal(t, c.Global.ContainerName, actualContainerName)
   124  	assert.Equal(t, c.Global.LocalIP, actualLocalIP)
   125  }
   126  
   127  func Test_serverConfigPath(t *testing.T) {
   128  	oldServerConfigPath := ServerConfigPath
   129  	ServerConfigPath = "./testdata/trpc_go.yaml"
   130  	path := serverConfigPath()
   131  	assert.Equal(t, "./testdata/trpc_go.yaml", path)
   132  
   133  	ServerConfigPath = "./trpc_go.yaml"
   134  	path = serverConfigPath()
   135  	assert.Equal(t, defaultConfigPath, path)
   136  
   137  	ServerConfigPath = oldServerConfigPath
   138  }
   139  
   140  func Test_setDefault(t *testing.T) {
   141  	var (
   142  		dstEmpty    string
   143  		dstNotEmpty = "not-empty"
   144  		def         = "default"
   145  	)
   146  	setDefault(&dstEmpty, def)
   147  	setDefault(&dstNotEmpty, def)
   148  	assert.Equal(t, dstEmpty, def)
   149  	assert.NotEqual(t, dstNotEmpty, def)
   150  }
   151  
   152  func TestConfigTransport(t *testing.T) {
   153  	t.Run("Server Config", func(t *testing.T) {
   154  		var cfg Config
   155  		require.Nil(t, yaml.Unmarshal([]byte(`
   156  server: 
   157    transport: test-transport
   158  `), &cfg))
   159  		require.Equal(t, "test-transport", cfg.Server.Transport)
   160  	})
   161  	t.Run("Service Config", func(t *testing.T) {
   162  		var cfg ServiceConfig
   163  		require.Nil(t, yaml.Unmarshal([]byte(`
   164  transport: test-transport
   165  `), &cfg))
   166  		require.Equal(t, "test-transport", cfg.Transport)
   167  	})
   168  	t.Run("Client Config", func(t *testing.T) {
   169  		var cfg ClientConfig
   170  		require.Nil(t, yaml.Unmarshal([]byte(`
   171  transport: test-transport
   172  `), &cfg))
   173  		require.Equal(t, "test-transport", cfg.Transport)
   174  	})
   175  }
   176  func TestConfigStreamFilter(t *testing.T) {
   177  	filterName := "sf1"
   178  	t.Run("server config", func(t *testing.T) {
   179  		var cfg Config
   180  		require.Nil(t, yaml.Unmarshal([]byte(`
   181  server:
   182    stream_filter:
   183      - sf1
   184  `), &cfg))
   185  		require.Equal(t, filterName, cfg.Server.StreamFilter[0])
   186  	})
   187  	t.Run("service config", func(t *testing.T) {
   188  		var cfg ServiceConfig
   189  		require.Nil(t, yaml.Unmarshal([]byte(`
   190  stream_filter:
   191    - sf1
   192  `), &cfg))
   193  		require.Equal(t, filterName, cfg.StreamFilter[0])
   194  	})
   195  	t.Run("client config", func(t *testing.T) {
   196  		var cfg ClientConfig
   197  		require.Nil(t, yaml.Unmarshal([]byte(`
   198  stream_filter:
   199    - sf1
   200  `), &cfg))
   201  		require.Equal(t, filterName, cfg.StreamFilter[0])
   202  	})
   203  
   204  }
   205  
   206  func TestRecordWhen(t *testing.T) {
   207  	t.Run("empty record-when", func(t *testing.T) {
   208  		config := &RPCZConfig{}
   209  		require.Nil(t, yaml.Unmarshal(
   210  			[]byte(`
   211  fraction: 1.0
   212  capacity: 10`),
   213  			config,
   214  		))
   215  		r := rpcz.NewRPCZ(config.generate())
   216  		var ids []rpcz.SpanID
   217  		for i := 0; i < 10; i++ {
   218  			s, ender := r.NewChild("")
   219  			ids = append(ids, s.ID())
   220  			ender.End()
   221  		}
   222  		for _, id := range ids {
   223  			_, ok := r.Query(id)
   224  			require.True(t, ok)
   225  		}
   226  	})
   227  	t.Run("unknown node", func(t *testing.T) {
   228  		config := &RecordWhenConfig{}
   229  		err := yaml.Unmarshal(
   230  			[]byte(`
   231  - XOR:
   232      - __min_request_size: 30
   233      - __min_response_size: 40
   234  `),
   235  			config,
   236  		)
   237  		require.Contains(t, errs.Msg(err), "unknown node: XOR")
   238  	})
   239  	t.Run("AND node is map type", func(t *testing.T) {
   240  		config := &RecordWhenConfig{}
   241  		err := yaml.Unmarshal(
   242  			[]byte(`
   243  - AND: {__rpc_name: "/trpc.app.server.service/method"}
   244  `),
   245  			config,
   246  		)
   247  		require.Contains(t, errs.Msg(err), "cannot unmarshal !!map into []map[trpc.nodeKind]yaml.Node")
   248  	})
   249  	t.Run("OR node is map type", func(t *testing.T) {
   250  		config := &RecordWhenConfig{}
   251  		err := yaml.Unmarshal(
   252  			[]byte(`
   253  - OR: {__rpc_name: "/trpc.app.server.service/method"}
   254  `),
   255  			config,
   256  		)
   257  		require.Contains(t, errs.Msg(err), "cannot unmarshal !!map into []map[trpc.nodeKind]yaml.Node")
   258  	})
   259  
   260  }
   261  func TestRecordWhen_NotNode(t *testing.T) {
   262  	t.Run("NOT node is empty", func(t *testing.T) {
   263  		config := &RPCZConfig{}
   264  		err := yaml.Unmarshal([]byte(`
   265  record_when:
   266    - NOT:
   267  `),
   268  			config,
   269  		)
   270  		require.ErrorContains(t, err, "value is empty")
   271  	})
   272  	t.Run("NOT node has two children", func(t *testing.T) {
   273  		config := &RecordWhenConfig{}
   274  		err := yaml.Unmarshal(
   275  			[]byte(`
   276      - NOT: {__rpc_name: "/trpc.app.server.service/method", __min_duration: 1000ms}
   277      `),
   278  			config,
   279  		)
   280  		require.Contains(t, errs.Msg(err), "the valid number of child node can only be 1")
   281  	})
   282  	t.Run("NOT has a leaf child", func(t *testing.T) {
   283  		config := &RecordWhenConfig{}
   284  		require.Nil(t, yaml.Unmarshal(
   285  			[]byte(`
   286      - NOT:
   287          __rpc_name: "/trpc.app.server.service/method"
   288      `),
   289  			config,
   290  		))
   291  	})
   292  	t.Run("NOT has a internal child", func(t *testing.T) {
   293  		config := &RecordWhenConfig{}
   294  		require.Nil(t, yaml.Unmarshal(
   295  			[]byte(`
   296  - NOT:
   297      OR:
   298        - __min_duration: 1000ms
   299        - __rpc_name: "/trpc.app.server.service/method"
   300  `),
   301  			config,
   302  		))
   303  	})
   304  	t.Run("NOT node is slice type", func(t *testing.T) {
   305  		config := &RecordWhenConfig{}
   306  		err := yaml.Unmarshal(
   307  			[]byte(`
   308  - NOT: 
   309      - __rpc_name: "/trpc.app.server.service/method"
   310  `),
   311  			config,
   312  		)
   313  		require.Contains(t, errs.Msg(err), "cannot unmarshal !!seq into map[trpc.nodeKind]yaml.Node")
   314  	})
   315  }
   316  func TestRecordWhen_ANDNode(t *testing.T) {
   317  	t.Run("AND node is empty", func(t *testing.T) {
   318  		config := &RPCZConfig{}
   319  		err := yaml.Unmarshal([]byte(`
   320  record_when:
   321    - AND:
   322  `),
   323  			config,
   324  		)
   325  		require.ErrorContains(t, err, "value is empty")
   326  	})
   327  	t.Run("AND node has two children", func(t *testing.T) {
   328  		config := &RecordWhenConfig{}
   329  		require.Nil(t, yaml.Unmarshal(
   330  			[]byte(`
   331  - AND: 
   332      - __rpc_name: "/trpc.app.server.service/method" 
   333      - __min_duration: 1000ms
   334  `),
   335  			config,
   336  		))
   337  	})
   338  	t.Run("AND has a leaf child", func(t *testing.T) {
   339  		config := &RecordWhenConfig{}
   340  		require.Nil(t, yaml.Unmarshal(
   341  			[]byte(`
   342  - AND:
   343      - __rpc_name: "/trpc.app.server.service/method"
   344  `),
   345  			config,
   346  		))
   347  	})
   348  	t.Run("AND has a internal child", func(t *testing.T) {
   349  		config := &RecordWhenConfig{}
   350  		require.Nil(t, yaml.Unmarshal(
   351  			[]byte(`
   352  - AND:
   353      - OR:
   354          - __min_duration: 1000ms
   355          - __rpc_name: "/trpc.app.server.service/method"
   356  `),
   357  			config,
   358  		))
   359  	})
   360  	t.Run("AND node is map type", func(t *testing.T) {
   361  		config := &RecordWhenConfig{}
   362  		err := yaml.Unmarshal(
   363  			[]byte(`
   364  - AND: 
   365      __rpc_name: "/trpc.app.server.service/method"
   366  `),
   367  			config,
   368  		)
   369  		require.Contains(t, errs.Msg(err), "cannot unmarshal !!map into []map[trpc.nodeKind]yaml.Node")
   370  	})
   371  }
   372  func TestRPCZ_RecordWhen_ErrorCode(t *testing.T) {
   373  	config := &RPCZConfig{}
   374  	mustYamlUnmarshal(t, []byte(`
   375  fraction: 1.0
   376  capacity: 10
   377  record_when:
   378    - __sampling_fraction: 1
   379    - OR:
   380        - __error_code: 1  # RetServerDecodeFail = 1
   381        - __error_code: 2  # RetServerEncodeFail = 2
   382        - __error_message: "service codec"
   383        - __error_message: "client codec"
   384    - NOT:
   385        OR:
   386          - __error_code: 1
   387          - __error_message: "service codec"
   388  `), config)
   389  
   390  	r := rpcz.NewRPCZ(config.generate())
   391  	var (
   392  		expectedIDs   []rpcz.SpanID
   393  		unexpectedIDs []rpcz.SpanID
   394  	)
   395  	{
   396  		s, ender := r.NewChild("")
   397  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetServerDecodeFail, "service codec"))
   398  		unexpectedIDs = append(unexpectedIDs, s.ID())
   399  		ender.End()
   400  	}
   401  	{
   402  		s, ender := r.NewChild("")
   403  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetServerEncodeFail, "service codec"))
   404  		unexpectedIDs = append(unexpectedIDs, s.ID())
   405  		ender.End()
   406  	}
   407  	{
   408  		s, ender := r.NewChild("")
   409  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetServerDecodeFail, "client codec"))
   410  		unexpectedIDs = append(unexpectedIDs, s.ID())
   411  		ender.End()
   412  	}
   413  	{
   414  		s, ender := r.NewChild("")
   415  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetServerEncodeFail, "client codec"))
   416  		expectedIDs = append(expectedIDs, s.ID())
   417  		ender.End()
   418  	}
   419  	for i, id := range expectedIDs {
   420  		_, ok := r.Query(id)
   421  		require.True(t, ok, i)
   422  	}
   423  	for i, id := range unexpectedIDs {
   424  		_, ok := r.Query(id)
   425  		require.False(t, ok, i)
   426  	}
   427  }
   428  func TestRPC_RecordWhen_CustomAttribute(t *testing.T) {
   429  	config := &RPCZConfig{}
   430  	mustYamlUnmarshal(t, []byte(`
   431  fraction: 1.0
   432  capacity: 10
   433  record_when:
   434    - __sampling_fraction: 1
   435    - OR:
   436        - __has_attribute: (race, elf)
   437        - __has_attribute: (class, wizard)
   438    - NOT:
   439        OR:
   440          - __has_attribute: (race, dwarf)
   441          - __has_attribute: (class, warlock)
   442  `), config)
   443  
   444  	r := rpcz.NewRPCZ(config.generate())
   445  	var (
   446  		expectedIDs   []rpcz.SpanID
   447  		unexpectedIDs []rpcz.SpanID
   448  	)
   449  	{
   450  		s, ender := r.NewChild("")
   451  		s.SetAttribute("race", "elf")
   452  		s.SetAttribute("class", "wizard")
   453  		expectedIDs = append(expectedIDs, s.ID())
   454  		ender.End()
   455  	}
   456  	{
   457  		s, ender := r.NewChild("")
   458  		s.SetAttribute("race", "elf")
   459  		s.SetAttribute("class", "wizard, warlock")
   460  		unexpectedIDs = append(unexpectedIDs, s.ID())
   461  		ender.End()
   462  	}
   463  	{
   464  		s, ender := r.NewChild("")
   465  		s.SetAttribute("race", "elf, dwarf")
   466  		s.SetAttribute("class", "wizard")
   467  		unexpectedIDs = append(unexpectedIDs, s.ID())
   468  		ender.End()
   469  	}
   470  	{
   471  		s, ender := r.NewChild("")
   472  		s.SetAttribute("race", "elf, dwarf")
   473  		s.SetAttribute("class", "wizard, warlock")
   474  		unexpectedIDs = append(unexpectedIDs, s.ID())
   475  		ender.End()
   476  	}
   477  	for i, id := range expectedIDs {
   478  		_, ok := r.Query(id)
   479  		require.True(t, ok, i)
   480  	}
   481  	for i, id := range unexpectedIDs {
   482  		_, ok := r.Query(id)
   483  		require.False(t, ok, i)
   484  	}
   485  }
   486  func TestRPC_RecordWhen_InvalidCustomAttribute(t *testing.T) {
   487  	t.Run("miss left parenthesis", func(t *testing.T) {
   488  		config := &RPCZConfig{}
   489  		require.ErrorContains(t, yaml.Unmarshal([]byte(`
   490  record_when:
   491    - __has_attribute: race, elf)
   492  `), config), "invalid attribute form")
   493  	})
   494  	t.Run("miss right parenthesis", func(t *testing.T) {
   495  		config := &RPCZConfig{}
   496  		require.ErrorContains(t, yaml.Unmarshal([]byte(`
   497  record_when:
   498    - __has_attribute: (race, elf
   499  `), config), "invalid attribute form")
   500  	})
   501  	t.Run("middle delimiter space", func(t *testing.T) {
   502  		config := &RPCZConfig{}
   503  		require.ErrorContains(t, yaml.Unmarshal([]byte(`
   504  record_when:
   505    - __has_attribute: (race,elf)
   506  `), config), "invalid attribute form")
   507  	})
   508  	t.Run("middle delimiter comma", func(t *testing.T) {
   509  		config := &RPCZConfig{}
   510  		require.ErrorContains(t, yaml.Unmarshal([]byte(`
   511  record_when:
   512    - __has_attribute: (race elf)
   513  `), config), "invalid attribute form")
   514  	})
   515  }
   516  func TestRPCZ_RecordWhen_MinDuration(t *testing.T) {
   517  	t.Run("not empty", func(t *testing.T) {
   518  		config := &RPCZConfig{}
   519  		mustYamlUnmarshal(t, []byte(`
   520  fraction: 1.0
   521  capacity: 10
   522  record_when:
   523    - __error_code: 999 # RetUnknown = 0
   524    - __min_duration: 100ms
   525    - __sampling_fraction: 1
   526  `), config)
   527  
   528  		r := rpcz.NewRPCZ(config.generate())
   529  		var (
   530  			expectedIDs   []rpcz.SpanID
   531  			unexpectedIDs []rpcz.SpanID
   532  		)
   533  		{
   534  			s, ender := r.NewChild("")
   535  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   536  			// mimic some time-consuming operation.
   537  			time.Sleep(1 * time.Second)
   538  			expectedIDs = append(expectedIDs, s.ID())
   539  			ender.End()
   540  		}
   541  		{
   542  			s, ender := r.NewChild("")
   543  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   544  			// mimic some time-consuming operation.
   545  			time.Sleep(2 * time.Second)
   546  			expectedIDs = append(expectedIDs, s.ID())
   547  			ender.End()
   548  		}
   549  		{
   550  			// don't call ender.End()
   551  			s, _ := r.NewChild("")
   552  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetOK, ""))
   553  			unexpectedIDs = append(unexpectedIDs, s.ID())
   554  		}
   555  		{
   556  			s, ender := r.NewChild("")
   557  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   558  			// mimic some time-consuming operation.
   559  			time.Sleep(1 * time.Millisecond)
   560  			unexpectedIDs = append(unexpectedIDs, s.ID())
   561  			ender.End()
   562  		}
   563  		{
   564  			s, ender := r.NewChild("")
   565  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   566  			// mimic some time-consuming operation.
   567  			time.Sleep(2 * time.Millisecond)
   568  			unexpectedIDs = append(unexpectedIDs, s.ID())
   569  			ender.End()
   570  		}
   571  		for _, id := range expectedIDs {
   572  			_, ok := r.Query(id)
   573  			require.True(t, ok)
   574  		}
   575  		for _, id := range unexpectedIDs {
   576  			_, ok := r.Query(id)
   577  			require.False(t, ok)
   578  		}
   579  	})
   580  	t.Run("empty", func(t *testing.T) {
   581  		config := &RPCZConfig{}
   582  		mustYamlUnmarshal(t, []byte(`
   583  fraction: 1.0
   584  capacity: 10
   585  record_when:
   586    - __error_code: 0 # RetOK = 0
   587    - __sampling_fraction: 1
   588  `), config)
   589  
   590  		r := rpcz.NewRPCZ(config.generate())
   591  		var (
   592  			unexpectedID rpcz.SpanID
   593  			expectedID   rpcz.SpanID
   594  		)
   595  		{
   596  			s, ender := r.NewChild("")
   597  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   598  			// mimic some time-consuming operation.
   599  			time.Sleep(1 * time.Millisecond)
   600  			unexpectedID = s.ID()
   601  			ender.End()
   602  		}
   603  		{
   604  			s, ender := r.NewChild("")
   605  			s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetOK, ""))
   606  			// mimic some time-consuming operation.
   607  			time.Sleep(1 * time.Millisecond)
   608  			expectedID = s.ID()
   609  			ender.End()
   610  		}
   611  		_, ok := r.Query(unexpectedID)
   612  		require.False(t, ok)
   613  
   614  		_, ok = r.Query(expectedID)
   615  		require.True(t, ok)
   616  	})
   617  }
   618  func TestRPCZ_RecordWhen_MinRequestSize(t *testing.T) {
   619  	config := &RPCZConfig{}
   620  	mustYamlUnmarshal(t, []byte(`
   621  fraction: 1.0
   622  capacity: 10
   623  record_when:
   624    - __sampling_fraction: 1
   625    - __min_request_size: 30
   626  `), config)
   627  
   628  	r := rpcz.NewRPCZ(config.generate())
   629  	t.Run("unset request size", func(t *testing.T) {
   630  		s, ender := r.NewChild("")
   631  		unexpectedID := s.ID()
   632  		ender.End()
   633  		_, ok := r.Query(unexpectedID)
   634  		require.False(t, ok)
   635  	})
   636  	t.Run("request size less than min_request_size", func(t *testing.T) {
   637  		s, ender := r.NewChild("")
   638  		s.SetAttribute(rpcz.TRPCAttributeRequestSize, 29)
   639  		unexpectedID := s.ID()
   640  		ender.End()
   641  		_, ok := r.Query(unexpectedID)
   642  		require.False(t, ok)
   643  	})
   644  	t.Run("request size equals to min_request_size", func(t *testing.T) {
   645  		s, ender := r.NewChild("")
   646  		s.SetAttribute(rpcz.TRPCAttributeRequestSize, 30)
   647  		expectedID := s.ID()
   648  		ender.End()
   649  		_, ok := r.Query(expectedID)
   650  		require.True(t, ok)
   651  	})
   652  	t.Run("request size greater than min_request_size", func(t *testing.T) {
   653  		s, ender := r.NewChild("")
   654  		s.SetAttribute(rpcz.TRPCAttributeRequestSize, 31)
   655  		expectedID := s.ID()
   656  		ender.End()
   657  		_, ok := r.Query(expectedID)
   658  		require.True(t, ok)
   659  	})
   660  }
   661  func TestRPCZ_RecordWhen_MinResponseSize(t *testing.T) {
   662  	config := &RPCZConfig{}
   663  	mustYamlUnmarshal(t, []byte(`
   664  fraction: 1.0
   665  capacity: 10
   666  record_when:
   667    - __sampling_fraction: 1
   668    - __min_response_size: 40
   669  `), config)
   670  
   671  	r := rpcz.NewRPCZ(config.generate())
   672  	t.Run("unset response size", func(t *testing.T) {
   673  		s, ender := r.NewChild("")
   674  		unexpectedID := s.ID()
   675  		ender.End()
   676  		_, ok := r.Query(unexpectedID)
   677  		require.False(t, ok)
   678  	})
   679  	t.Run("request size less than min_response_size", func(t *testing.T) {
   680  		s, ender := r.NewChild("")
   681  		s.SetAttribute(rpcz.TRPCAttributeResponseSize, 39)
   682  		unexpectedID := s.ID()
   683  		ender.End()
   684  		_, ok := r.Query(unexpectedID)
   685  		require.False(t, ok)
   686  	})
   687  	t.Run("request size equals to min_response_size", func(t *testing.T) {
   688  		s, ender := r.NewChild("")
   689  		s.SetAttribute(rpcz.TRPCAttributeResponseSize, 40)
   690  		expectedID := s.ID()
   691  		ender.End()
   692  		_, ok := r.Query(expectedID)
   693  		require.True(t, ok)
   694  	})
   695  	t.Run("request size greater than min_response_size", func(t *testing.T) {
   696  		s, ender := r.NewChild("")
   697  		s.SetAttribute(rpcz.TRPCAttributeResponseSize, 41)
   698  		expectedID := s.ID()
   699  		ender.End()
   700  		_, ok := r.Query(expectedID)
   701  		require.True(t, ok)
   702  	})
   703  }
   704  func TestRPCZ_RecordWhen_RPCName(t *testing.T) {
   705  	config := &RPCZConfig{}
   706  	mustYamlUnmarshal(t, []byte(`
   707  fraction: 1.0
   708  capacity: 10
   709  record_when:
   710    - __sampling_fraction: 1
   711    - __rpc_name: trpc.app.server.service
   712  `), config)
   713  
   714  	r := rpcz.NewRPCZ(config.generate())
   715  	t.Run("unset RPCName", func(t *testing.T) {
   716  		s, ender := r.NewChild("")
   717  		unexpectedID := s.ID()
   718  		ender.End()
   719  		_, ok := r.Query(unexpectedID)
   720  		require.False(t, ok)
   721  	})
   722  	t.Run("RPCName does not contain rpc_name", func(t *testing.T) {
   723  		s, ender := r.NewChild("")
   724  		s.SetAttribute(rpcz.TRPCAttributeRPCName, "/xxx.app.server.service/method")
   725  		unexpectedID := s.ID()
   726  		ender.End()
   727  		_, ok := r.Query(unexpectedID)
   728  		require.False(t, ok)
   729  	})
   730  	t.Run("RPCName contains  rpc_name", func(t *testing.T) {
   731  		s, ender := r.NewChild("")
   732  		s.SetAttribute(rpcz.TRPCAttributeRPCName, "/trpc.app.server.service/method")
   733  		expectedID := s.ID()
   734  		ender.End()
   735  		_, ok := r.Query(expectedID)
   736  		require.True(t, ok)
   737  	})
   738  }
   739  func TestRPCZ_RecordWhen_ErrorCodeAndMinDuration(t *testing.T) {
   740  	config := &RPCZConfig{}
   741  	mustYamlUnmarshal(t, []byte(`
   742  fraction: 1.0
   743  capacity: 10
   744  record_when:
   745    - AND:
   746        - __error_code: 0 # RetOK = 0
   747        - __min_duration: 100ms
   748    - __sampling_fraction: 1
   749  `), config)
   750  
   751  	r := rpcz.NewRPCZ(config.generate())
   752  	var (
   753  		expectedIDs   []rpcz.SpanID
   754  		unexpectedIDs []rpcz.SpanID
   755  	)
   756  	{
   757  		s, ender := r.NewChild("")
   758  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetOK, ""))
   759  		// mimic some time-consuming operation.
   760  		time.Sleep(1 * time.Second)
   761  		expectedIDs = append(expectedIDs, s.ID())
   762  		ender.End()
   763  	}
   764  
   765  	{
   766  		s, ender := r.NewChild("")
   767  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   768  		// mimic some time-consuming operation.
   769  		time.Sleep(2 * time.Second)
   770  		unexpectedIDs = append(unexpectedIDs, s.ID())
   771  		ender.End()
   772  	}
   773  	{
   774  		s, ender := r.NewChild("")
   775  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetOK, ""))
   776  		// mimic some time-consuming operation.
   777  		time.Sleep(1 * time.Millisecond)
   778  		unexpectedIDs = append(unexpectedIDs, s.ID())
   779  		ender.End()
   780  	}
   781  	{
   782  		s, ender := r.NewChild("")
   783  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetUnknown, ""))
   784  		// mimic some time-consuming operation.
   785  		time.Sleep(2 * time.Millisecond)
   786  		unexpectedIDs = append(unexpectedIDs, s.ID())
   787  		ender.End()
   788  	}
   789  	{
   790  		// don't call ender.End()
   791  		s, _ := r.NewChild("")
   792  		s.SetAttribute(rpcz.TRPCAttributeError, errs.NewFrameError(errs.RetOK, ""))
   793  		unexpectedIDs = append(unexpectedIDs, s.ID())
   794  	}
   795  	for i, id := range expectedIDs {
   796  		_, ok := r.Query(id)
   797  		require.True(t, ok, i)
   798  	}
   799  	for i, id := range unexpectedIDs {
   800  		_, ok := r.Query(id)
   801  		require.False(t, ok, i)
   802  	}
   803  }
   804  
   805  func mustYamlUnmarshal(t *testing.T, in []byte, out interface{}) {
   806  	t.Helper()
   807  	if err := yaml.Unmarshal(in, out); err != nil {
   808  		t.Fatal(err)
   809  	}
   810  }
   811  func TestRepairServiceIdleTime(t *testing.T) {
   812  	t.Run("set by service timeout", func(t *testing.T) {
   813  		var cfg Config
   814  		require.Nil(t, yaml.Unmarshal([]byte(`
   815  server: 
   816    service:  
   817      - name: trpc.test.helloworld.Greeter
   818        ip: 127.0.0.1
   819        port: 8000
   820        network: tcp
   821        protocol: trpc
   822        timeout: 120000
   823  `), &cfg))
   824  		require.Nil(t, RepairConfig(&cfg))
   825  		require.Equal(t, 120000, cfg.Server.Service[0].Idletime)
   826  	})
   827  	t.Run("set by default", func(t *testing.T) {
   828  		var cfg Config
   829  		require.Nil(t, yaml.Unmarshal([]byte(`
   830  server: 
   831    service:  
   832      - name: trpc.test.helloworld.Greeter
   833        ip: 127.0.0.1
   834        port: 8000
   835        network: tcp
   836        protocol: trpc
   837        timeout: 500
   838  `), &cfg))
   839  		require.Nil(t, RepairConfig(&cfg))
   840  		require.Equal(t, defaultIdleTimeout, cfg.Server.Service[0].Idletime)
   841  	})
   842  	t.Run("set by config", func(t *testing.T) {
   843  		var cfg Config
   844  		require.Nil(t, yaml.Unmarshal([]byte(`
   845  server: 
   846    service:  
   847      - name: trpc.test.helloworld.Greeter
   848        ip: 127.0.0.1
   849        port: 8000
   850        network: tcp
   851        protocol: trpc
   852        timeout: 500
   853        idletime: 1500
   854  `), &cfg))
   855  		require.Nil(t, RepairConfig(&cfg))
   856  		require.Equal(t, 1500, cfg.Server.Service[0].Idletime)
   857  	})
   858  }