...

Source file src/go.opentelemetry.io/otel/baggage/baggage_test.go

Documentation: go.opentelemetry.io/otel/baggage

     1  // Copyright The OpenTelemetry Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package baggage
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"sort"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"go.opentelemetry.io/otel/internal/baggage"
    27  )
    28  
    29  var rng *rand.Rand
    30  
    31  func init() {
    32  	// Seed with a static value to ensure deterministic results.
    33  	rng = rand.New(rand.NewSource(1))
    34  }
    35  
    36  func TestKeyRegExp(t *testing.T) {
    37  	// ASCII only
    38  	invalidKeyRune := []rune{
    39  		'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
    40  		'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F',
    41  		'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
    42  		'\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', ' ',
    43  		'(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?',
    44  		'=', '{', '}', '\x7F',
    45  	}
    46  
    47  	for _, ch := range invalidKeyRune {
    48  		assert.NotRegexp(t, keyDef, fmt.Sprintf("%c", ch))
    49  	}
    50  }
    51  
    52  func TestValueRegExp(t *testing.T) {
    53  	// ASCII only
    54  	invalidValueRune := []rune{
    55  		'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
    56  		'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F',
    57  		'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
    58  		'\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', ' ',
    59  		'"', ',', ';', '\\', '\x7F',
    60  	}
    61  
    62  	for _, ch := range invalidValueRune {
    63  		assert.NotRegexp(t, `^`+valueDef+`$`, fmt.Sprintf("invalid-%c-value", ch))
    64  	}
    65  }
    66  
    67  func TestParseProperty(t *testing.T) {
    68  	p := Property{key: "key", value: "value", hasValue: true}
    69  
    70  	testcases := []struct {
    71  		in       string
    72  		expected Property
    73  	}{
    74  		{
    75  			in:       "",
    76  			expected: Property{},
    77  		},
    78  		{
    79  			in: "key",
    80  			expected: Property{
    81  				key: "key",
    82  			},
    83  		},
    84  		{
    85  			in: "key=",
    86  			expected: Property{
    87  				key:      "key",
    88  				hasValue: true,
    89  			},
    90  		},
    91  		{
    92  			in:       "key=value",
    93  			expected: p,
    94  		},
    95  		{
    96  			in:       " key=value ",
    97  			expected: p,
    98  		},
    99  		{
   100  			in:       "key = value",
   101  			expected: p,
   102  		},
   103  		{
   104  			in:       " key = value ",
   105  			expected: p,
   106  		},
   107  		{
   108  			in:       "\tkey=value",
   109  			expected: p,
   110  		},
   111  	}
   112  
   113  	for _, tc := range testcases {
   114  		actual, err := parseProperty(tc.in)
   115  
   116  		if !assert.NoError(t, err) {
   117  			continue
   118  		}
   119  
   120  		assert.Equal(t, tc.expected.Key(), actual.Key(), tc.in)
   121  
   122  		actualV, actualOk := actual.Value()
   123  		expectedV, expectedOk := tc.expected.Value()
   124  		assert.Equal(t, expectedOk, actualOk, tc.in)
   125  		assert.Equal(t, expectedV, actualV, tc.in)
   126  	}
   127  }
   128  
   129  func TestParsePropertyError(t *testing.T) {
   130  	_, err := parseProperty(",;,")
   131  	assert.ErrorIs(t, err, errInvalidProperty)
   132  }
   133  
   134  func TestNewKeyProperty(t *testing.T) {
   135  	p, err := NewKeyProperty(" ")
   136  	assert.ErrorIs(t, err, errInvalidKey)
   137  	assert.Equal(t, Property{}, p)
   138  
   139  	p, err = NewKeyProperty("key")
   140  	assert.NoError(t, err)
   141  	assert.Equal(t, Property{key: "key"}, p)
   142  }
   143  
   144  func TestNewKeyValueProperty(t *testing.T) {
   145  	p, err := NewKeyValueProperty(" ", "")
   146  	assert.ErrorIs(t, err, errInvalidKey)
   147  	assert.Equal(t, Property{}, p)
   148  
   149  	p, err = NewKeyValueProperty("key", ";")
   150  	assert.ErrorIs(t, err, errInvalidValue)
   151  	assert.Equal(t, Property{}, p)
   152  
   153  	p, err = NewKeyValueProperty("key", "value")
   154  	assert.NoError(t, err)
   155  	assert.Equal(t, Property{key: "key", value: "value", hasValue: true}, p)
   156  }
   157  
   158  func TestPropertyValidate(t *testing.T) {
   159  	p := Property{}
   160  	assert.ErrorIs(t, p.validate(), errInvalidKey)
   161  
   162  	p.key = "k"
   163  	assert.NoError(t, p.validate())
   164  
   165  	p.value = ";"
   166  	assert.EqualError(t, p.validate(), "invalid property: inconsistent value")
   167  
   168  	p.hasValue = true
   169  	assert.ErrorIs(t, p.validate(), errInvalidValue)
   170  
   171  	p.value = "v"
   172  	assert.NoError(t, p.validate())
   173  }
   174  
   175  func TestNewEmptyBaggage(t *testing.T) {
   176  	b, err := New()
   177  	assert.NoError(t, err)
   178  	assert.Equal(t, Baggage{}, b)
   179  }
   180  
   181  func TestNewBaggage(t *testing.T) {
   182  	b, err := New(Member{key: "k", hasData: true})
   183  	assert.NoError(t, err)
   184  	assert.Equal(t, Baggage{list: baggage.List{"k": {}}}, b)
   185  }
   186  
   187  func TestNewBaggageWithDuplicates(t *testing.T) {
   188  	// Having this many members would normally cause this to error, but since
   189  	// these are duplicates of the same key they will be collapsed into a
   190  	// single entry.
   191  	m := make([]Member, maxMembers+1)
   192  	for i := range m {
   193  		// Duplicates are collapsed.
   194  		m[i] = Member{
   195  			key:     "a",
   196  			value:   fmt.Sprintf("%d", i),
   197  			hasData: true,
   198  		}
   199  	}
   200  	b, err := New(m...)
   201  	assert.NoError(t, err)
   202  
   203  	// Ensure that the last-one-wins by verifying the value.
   204  	v := fmt.Sprintf("%d", maxMembers)
   205  	want := Baggage{list: baggage.List{"a": {Value: v}}}
   206  	assert.Equal(t, want, b)
   207  }
   208  
   209  func TestNewBaggageErrorEmptyMember(t *testing.T) {
   210  	_, err := New(Member{})
   211  	assert.ErrorIs(t, err, errInvalidMember)
   212  }
   213  
   214  func key(n int) string {
   215  	r := []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
   216  
   217  	b := make([]rune, n)
   218  	for i := range b {
   219  		b[i] = r[rng.Intn(len(r))]
   220  	}
   221  	return string(b)
   222  }
   223  
   224  func TestNewBaggageErrorTooManyBytes(t *testing.T) {
   225  	m := make([]Member, (maxBytesPerBaggageString/maxBytesPerMembers)+1)
   226  	for i := range m {
   227  		m[i] = Member{key: key(maxBytesPerMembers), hasData: true}
   228  	}
   229  	_, err := New(m...)
   230  	assert.ErrorIs(t, err, errBaggageBytes)
   231  }
   232  
   233  func TestNewBaggageErrorTooManyMembers(t *testing.T) {
   234  	m := make([]Member, maxMembers+1)
   235  	for i := range m {
   236  		m[i] = Member{key: fmt.Sprintf("%d", i), hasData: true}
   237  	}
   238  	_, err := New(m...)
   239  	assert.ErrorIs(t, err, errMemberNumber)
   240  }
   241  
   242  func TestBaggageParse(t *testing.T) {
   243  	tooLarge := key(maxBytesPerBaggageString + 1)
   244  
   245  	tooLargeMember := key(maxBytesPerMembers + 1)
   246  
   247  	m := make([]string, maxMembers+1)
   248  	for i := range m {
   249  		m[i] = fmt.Sprintf("a%d=", i)
   250  	}
   251  	tooManyMembers := strings.Join(m, listDelimiter)
   252  
   253  	testcases := []struct {
   254  		name string
   255  		in   string
   256  		want baggage.List
   257  		err  error
   258  	}{
   259  		{
   260  			name: "empty value",
   261  			in:   "",
   262  			want: baggage.List(nil),
   263  		},
   264  		{
   265  			name: "single member empty value no properties",
   266  			in:   "foo=",
   267  			want: baggage.List{
   268  				"foo": {Value: ""},
   269  			},
   270  		},
   271  		{
   272  			name: "single member no properties",
   273  			in:   "foo=1",
   274  			want: baggage.List{
   275  				"foo": {Value: "1"},
   276  			},
   277  		},
   278  		{
   279  			name: "single member no properties plus",
   280  			in:   "foo=1+1",
   281  			want: baggage.List{
   282  				"foo": {Value: "1+1"},
   283  			},
   284  		},
   285  		{
   286  			name: "single member no properties plus encoded",
   287  			in:   "foo=1%2B1",
   288  			want: baggage.List{
   289  				"foo": {Value: "1+1"},
   290  			},
   291  		},
   292  		{
   293  			name: "single member no properties slash",
   294  			in:   "foo=1/1",
   295  			want: baggage.List{
   296  				"foo": {Value: "1/1"},
   297  			},
   298  		},
   299  		{
   300  			name: "single member no properties slash encoded",
   301  			in:   "foo=1%2F1",
   302  			want: baggage.List{
   303  				"foo": {Value: "1/1"},
   304  			},
   305  		},
   306  		{
   307  			name: "single member no properties equals",
   308  			in:   "foo=1=1",
   309  			want: baggage.List{
   310  				"foo": {Value: "1=1"},
   311  			},
   312  		},
   313  		{
   314  			name: "single member no properties equals encoded",
   315  			in:   "foo=1%3D1",
   316  			want: baggage.List{
   317  				"foo": {Value: "1=1"},
   318  			},
   319  		},
   320  		{
   321  			name: "single member with spaces",
   322  			in:   " foo \t= 1\t\t ",
   323  			want: baggage.List{
   324  				"foo": {Value: "1"},
   325  			},
   326  		},
   327  		{
   328  			name: "single member empty value with properties",
   329  			in:   "foo=;state=on;red",
   330  			want: baggage.List{
   331  				"foo": {
   332  					Value: "",
   333  					Properties: []baggage.Property{
   334  						{Key: "state", Value: "on", HasValue: true},
   335  						{Key: "red"},
   336  					},
   337  				},
   338  			},
   339  		},
   340  		{
   341  			name: "single member with properties",
   342  			in:   "foo=1;state=on;red",
   343  			want: baggage.List{
   344  				"foo": {
   345  					Value: "1",
   346  					Properties: []baggage.Property{
   347  						{Key: "state", Value: "on", HasValue: true},
   348  						{Key: "red"},
   349  					},
   350  				},
   351  			},
   352  		},
   353  		{
   354  			name: "single member with value containing equal signs",
   355  			in:   "foo=0=0=0",
   356  			want: baggage.List{
   357  				"foo": {Value: "0=0=0"},
   358  			},
   359  		},
   360  		{
   361  			name: "two members with properties",
   362  			in:   "foo=1;state=on;red,bar=2;yellow",
   363  			want: baggage.List{
   364  				"foo": {
   365  					Value: "1",
   366  					Properties: []baggage.Property{
   367  						{Key: "state", Value: "on", HasValue: true},
   368  						{Key: "red"},
   369  					},
   370  				},
   371  				"bar": {
   372  					Value:      "2",
   373  					Properties: []baggage.Property{{Key: "yellow"}},
   374  				},
   375  			},
   376  		},
   377  		{
   378  			// According to the OTel spec, last value wins.
   379  			name: "duplicate key",
   380  			in:   "foo=1;state=on;red,foo=2",
   381  			want: baggage.List{
   382  				"foo": {Value: "2"},
   383  			},
   384  		},
   385  		{
   386  			name: "url encoded value",
   387  			in:   "key1=val%252",
   388  			want: baggage.List{
   389  				"key1": {Value: "val%2"},
   390  			},
   391  		},
   392  		{
   393  			name: "invalid member: empty",
   394  			in:   "foo=,,bar=",
   395  			err:  errInvalidMember,
   396  		},
   397  		{
   398  			name: "invalid member: no key",
   399  			in:   "=foo",
   400  			err:  errInvalidKey,
   401  		},
   402  		{
   403  			name: "invalid member: no value",
   404  			in:   "foo",
   405  			err:  errInvalidMember,
   406  		},
   407  		{
   408  			name: "invalid member: invalid key",
   409  			in:   "\\=value",
   410  			err:  errInvalidKey,
   411  		},
   412  		{
   413  			name: "invalid member: invalid value",
   414  			in:   "foo=\\",
   415  			err:  errInvalidValue,
   416  		},
   417  		{
   418  			name: "invalid property: invalid key",
   419  			in:   "foo=1;=v",
   420  			err:  errInvalidProperty,
   421  		},
   422  		{
   423  			name: "invalid property: invalid value",
   424  			in:   "foo=1;key=\\",
   425  			err:  errInvalidProperty,
   426  		},
   427  		{
   428  			name: "invalid baggage string: too large",
   429  			in:   tooLarge,
   430  			err:  errBaggageBytes,
   431  		},
   432  		{
   433  			name: "invalid baggage string: member too large",
   434  			in:   tooLargeMember,
   435  			err:  errMemberBytes,
   436  		},
   437  		{
   438  			name: "invalid baggage string: too many members",
   439  			in:   tooManyMembers,
   440  			err:  errMemberNumber,
   441  		},
   442  	}
   443  
   444  	for _, tc := range testcases {
   445  		t.Run(tc.name, func(t *testing.T) {
   446  			actual, err := Parse(tc.in)
   447  			assert.ErrorIs(t, err, tc.err)
   448  			assert.Equal(t, Baggage{list: tc.want}, actual)
   449  		})
   450  	}
   451  }
   452  
   453  func TestBaggageString(t *testing.T) {
   454  	testcases := []struct {
   455  		name    string
   456  		out     string
   457  		baggage baggage.List
   458  	}{
   459  		{
   460  			name:    "empty value",
   461  			out:     "",
   462  			baggage: baggage.List(nil),
   463  		},
   464  		{
   465  			name: "single member empty value no properties",
   466  			out:  "foo=",
   467  			baggage: baggage.List{
   468  				"foo": {Value: ""},
   469  			},
   470  		},
   471  		{
   472  			name: "single member no properties",
   473  			out:  "foo=1",
   474  			baggage: baggage.List{
   475  				"foo": {Value: "1"},
   476  			},
   477  		},
   478  		{
   479  			name: "URL encoded value",
   480  			out:  "foo=1%3D1",
   481  			baggage: baggage.List{
   482  				"foo": {Value: "1=1"},
   483  			},
   484  		},
   485  		{
   486  			name: "plus",
   487  			out:  "foo=1%2B1",
   488  			baggage: baggage.List{
   489  				"foo": {Value: "1+1"},
   490  			},
   491  		},
   492  		{
   493  			name: "single member empty value with properties",
   494  			out:  "foo=;red;state=on",
   495  			baggage: baggage.List{
   496  				"foo": {
   497  					Value: "",
   498  					Properties: []baggage.Property{
   499  						{Key: "state", Value: "on", HasValue: true},
   500  						{Key: "red"},
   501  					},
   502  				},
   503  			},
   504  		},
   505  		{
   506  			name: "single member with properties",
   507  			// Properties are "opaque values" meaning they are sent as they
   508  			// are set and no encoding is performed.
   509  			out: "foo=1;red;state=on;z=z=z",
   510  			baggage: baggage.List{
   511  				"foo": {
   512  					Value: "1",
   513  					Properties: []baggage.Property{
   514  						{Key: "state", Value: "on", HasValue: true},
   515  						{Key: "red"},
   516  						{Key: "z", Value: "z=z", HasValue: true},
   517  					},
   518  				},
   519  			},
   520  		},
   521  		{
   522  			name: "two members with properties",
   523  			out:  "bar=2;yellow,foo=1;red;state=on",
   524  			baggage: baggage.List{
   525  				"foo": {
   526  					Value: "1",
   527  					Properties: []baggage.Property{
   528  						{Key: "state", Value: "on", HasValue: true},
   529  						{Key: "red"},
   530  					},
   531  				},
   532  				"bar": {
   533  					Value:      "2",
   534  					Properties: []baggage.Property{{Key: "yellow"}},
   535  				},
   536  			},
   537  		},
   538  	}
   539  
   540  	orderer := func(s string) string {
   541  		members := strings.Split(s, listDelimiter)
   542  		for i, m := range members {
   543  			parts := strings.Split(m, propertyDelimiter)
   544  			if len(parts) > 1 {
   545  				sort.Strings(parts[1:])
   546  				members[i] = strings.Join(parts, propertyDelimiter)
   547  			}
   548  		}
   549  		sort.Strings(members)
   550  		return strings.Join(members, listDelimiter)
   551  	}
   552  
   553  	for _, tc := range testcases {
   554  		b := Baggage{tc.baggage}
   555  		assert.Equal(t, tc.out, orderer(b.String()))
   556  	}
   557  }
   558  
   559  func TestBaggageLen(t *testing.T) {
   560  	b := Baggage{}
   561  	assert.Equal(t, 0, b.Len())
   562  
   563  	b.list = make(baggage.List, 1)
   564  	assert.Equal(t, 0, b.Len())
   565  
   566  	b.list["k"] = baggage.Item{}
   567  	assert.Equal(t, 1, b.Len())
   568  }
   569  
   570  func TestBaggageDeleteMember(t *testing.T) {
   571  	key := "k"
   572  
   573  	b0 := Baggage{}
   574  	b1 := b0.DeleteMember(key)
   575  	assert.NotContains(t, b1.list, key)
   576  
   577  	b0 = Baggage{list: baggage.List{
   578  		key:     {},
   579  		"other": {},
   580  	}}
   581  	b1 = b0.DeleteMember(key)
   582  	assert.Contains(t, b0.list, key)
   583  	assert.NotContains(t, b1.list, key)
   584  }
   585  
   586  func TestBaggageSetMemberEmpty(t *testing.T) {
   587  	_, err := Baggage{}.SetMember(Member{})
   588  	assert.ErrorIs(t, err, errInvalidMember)
   589  }
   590  
   591  func TestBaggageSetMember(t *testing.T) {
   592  	b0 := Baggage{}
   593  
   594  	key := "k"
   595  	m := Member{key: key, hasData: true}
   596  	b1, err := b0.SetMember(m)
   597  	assert.NoError(t, err)
   598  	assert.NotContains(t, b0.list, key)
   599  	assert.Equal(t, baggage.Item{}, b1.list[key])
   600  	assert.Equal(t, 0, len(b0.list))
   601  	assert.Equal(t, 1, len(b1.list))
   602  
   603  	m.value = "v"
   604  	b2, err := b1.SetMember(m)
   605  	assert.NoError(t, err)
   606  	assert.Equal(t, baggage.Item{}, b1.list[key])
   607  	assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key])
   608  	assert.Equal(t, 1, len(b1.list))
   609  	assert.Equal(t, 1, len(b2.list))
   610  
   611  	p := properties{{key: "p"}}
   612  	m.properties = p
   613  	b3, err := b2.SetMember(m)
   614  	assert.NoError(t, err)
   615  	assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key])
   616  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   617  	assert.Equal(t, 1, len(b2.list))
   618  	assert.Equal(t, 1, len(b3.list))
   619  
   620  	// The returned baggage needs to be immutable and should use a copy of the
   621  	// properties slice.
   622  	p[0] = Property{key: "different"}
   623  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   624  	// Reset for below.
   625  	p[0] = Property{key: "p"}
   626  
   627  	m = Member{key: "another", hasData: true}
   628  	b4, err := b3.SetMember(m)
   629  	assert.NoError(t, err)
   630  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   631  	assert.NotContains(t, b3.list, m.key)
   632  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b4.list[key])
   633  	assert.Equal(t, baggage.Item{}, b4.list[m.key])
   634  	assert.Equal(t, 1, len(b3.list))
   635  	assert.Equal(t, 2, len(b4.list))
   636  }
   637  
   638  func TestBaggageSetFalseMember(t *testing.T) {
   639  	b0 := Baggage{}
   640  
   641  	key := "k"
   642  	m := Member{key: key, hasData: false}
   643  	b1, err := b0.SetMember(m)
   644  	assert.Error(t, err)
   645  	assert.NotContains(t, b0.list, key)
   646  	assert.Equal(t, baggage.Item{}, b1.list[key])
   647  	assert.Equal(t, 0, len(b0.list))
   648  	assert.Equal(t, 0, len(b1.list))
   649  
   650  	m.value = "v"
   651  	b2, err := b1.SetMember(m)
   652  	assert.Error(t, err)
   653  	assert.Equal(t, baggage.Item{}, b1.list[key])
   654  	assert.Equal(t, baggage.Item{Value: ""}, b2.list[key])
   655  	assert.Equal(t, 0, len(b1.list))
   656  	assert.Equal(t, 0, len(b2.list))
   657  }
   658  
   659  func TestBaggageSetFalseMembers(t *testing.T) {
   660  	b0 := Baggage{}
   661  
   662  	key := "k"
   663  	m := Member{key: key, hasData: true}
   664  	b1, err := b0.SetMember(m)
   665  	assert.NoError(t, err)
   666  	assert.NotContains(t, b0.list, key)
   667  	assert.Equal(t, baggage.Item{}, b1.list[key])
   668  	assert.Equal(t, 0, len(b0.list))
   669  	assert.Equal(t, 1, len(b1.list))
   670  
   671  	m.value = "v"
   672  	b2, err := b1.SetMember(m)
   673  	assert.NoError(t, err)
   674  	assert.Equal(t, baggage.Item{}, b1.list[key])
   675  	assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key])
   676  	assert.Equal(t, 1, len(b1.list))
   677  	assert.Equal(t, 1, len(b2.list))
   678  
   679  	p := properties{{key: "p"}}
   680  	m.properties = p
   681  	b3, err := b2.SetMember(m)
   682  	assert.NoError(t, err)
   683  	assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key])
   684  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   685  	assert.Equal(t, 1, len(b2.list))
   686  	assert.Equal(t, 1, len(b3.list))
   687  
   688  	// The returned baggage needs to be immutable and should use a copy of the
   689  	// properties slice.
   690  	p[0] = Property{key: "different"}
   691  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   692  	// Reset for below.
   693  	p[0] = Property{key: "p"}
   694  
   695  	m = Member{key: "another"}
   696  	b4, err := b3.SetMember(m)
   697  	assert.Error(t, err)
   698  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key])
   699  	assert.NotContains(t, b3.list, m.key)
   700  	assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b4.list[key])
   701  	assert.Equal(t, baggage.Item{}, b4.list[m.key])
   702  	assert.Equal(t, 1, len(b3.list))
   703  	assert.Equal(t, 1, len(b4.list))
   704  }
   705  
   706  func TestNilBaggageMembers(t *testing.T) {
   707  	assert.Nil(t, Baggage{}.Members())
   708  }
   709  
   710  func TestBaggageMembers(t *testing.T) {
   711  	members := []Member{
   712  		{
   713  			key:   "foo",
   714  			value: "1",
   715  			properties: properties{
   716  				{key: "state", value: "on", hasValue: true},
   717  				{key: "red"},
   718  			},
   719  			hasData: true,
   720  		},
   721  		{
   722  			key:   "bar",
   723  			value: "2",
   724  			properties: properties{
   725  				{key: "yellow"},
   726  			},
   727  			hasData: true,
   728  		},
   729  	}
   730  
   731  	bag := Baggage{list: baggage.List{
   732  		"foo": {
   733  			Value: "1",
   734  			Properties: []baggage.Property{
   735  				{Key: "state", Value: "on", HasValue: true},
   736  				{Key: "red"},
   737  			},
   738  		},
   739  		"bar": {
   740  			Value:      "2",
   741  			Properties: []baggage.Property{{Key: "yellow"}},
   742  		},
   743  	}}
   744  
   745  	assert.ElementsMatch(t, members, bag.Members())
   746  }
   747  
   748  func TestBaggageMember(t *testing.T) {
   749  	bag := Baggage{list: baggage.List{"foo": {Value: "1"}}}
   750  	assert.Equal(t, Member{key: "foo", value: "1", hasData: true}, bag.Member("foo"))
   751  	assert.Equal(t, Member{}, bag.Member("bar"))
   752  }
   753  
   754  func TestMemberKey(t *testing.T) {
   755  	m := Member{}
   756  	assert.Equal(t, "", m.Key(), "even invalid values should be returned")
   757  
   758  	key := "k"
   759  	m.key = key
   760  	assert.Equal(t, key, m.Key())
   761  }
   762  
   763  func TestMemberValue(t *testing.T) {
   764  	m := Member{key: "k", value: "\\"}
   765  	assert.Equal(t, "\\", m.Value(), "even invalid values should be returned")
   766  
   767  	value := "v"
   768  	m.value = value
   769  	assert.Equal(t, value, m.Value())
   770  }
   771  
   772  func TestMemberProperties(t *testing.T) {
   773  	m := Member{key: "k", value: "v"}
   774  	assert.Nil(t, m.Properties())
   775  
   776  	p := []Property{{key: "foo"}}
   777  	m.properties = properties(p)
   778  	got := m.Properties()
   779  	assert.Equal(t, p, got)
   780  
   781  	// Returned slice needs to be a copy so the original is immutable.
   782  	got[0] = Property{key: "bar"}
   783  	assert.NotEqual(t, m.properties, got)
   784  }
   785  
   786  func TestMemberValidation(t *testing.T) {
   787  	m := Member{hasData: false}
   788  	assert.ErrorIs(t, m.validate(), errInvalidMember)
   789  
   790  	m.hasData = true
   791  	assert.ErrorIs(t, m.validate(), errInvalidKey)
   792  
   793  	m.key, m.value = "k", "\\"
   794  	assert.ErrorIs(t, m.validate(), errInvalidValue)
   795  
   796  	m.value = "v"
   797  	assert.NoError(t, m.validate())
   798  }
   799  
   800  func TestNewMember(t *testing.T) {
   801  	m, err := NewMember("", "")
   802  	assert.ErrorIs(t, err, errInvalidKey)
   803  	assert.Equal(t, Member{hasData: false}, m)
   804  
   805  	key, val := "k", "v"
   806  	p := Property{key: "foo"}
   807  	m, err = NewMember(key, val, p)
   808  	assert.NoError(t, err)
   809  	expected := Member{
   810  		key:        key,
   811  		value:      val,
   812  		properties: properties{{key: "foo"}},
   813  		hasData:    true,
   814  	}
   815  	assert.Equal(t, expected, m)
   816  
   817  	// wrong value with wrong decoding
   818  	val = "%zzzzz"
   819  	_, err = NewMember(key, val, p)
   820  	assert.ErrorIs(t, err, errInvalidValue)
   821  
   822  	// value should be decoded
   823  	val = "%3B"
   824  	m, err = NewMember(key, val, p)
   825  	expected = Member{
   826  		key:        key,
   827  		value:      ";",
   828  		properties: properties{{key: "foo"}},
   829  		hasData:    true,
   830  	}
   831  	assert.NoError(t, err)
   832  	assert.Equal(t, expected, m)
   833  
   834  	// Ensure new member is immutable.
   835  	p.key = "bar"
   836  	assert.Equal(t, expected, m)
   837  }
   838  
   839  func TestPropertiesValidate(t *testing.T) {
   840  	p := properties{{}}
   841  	assert.ErrorIs(t, p.validate(), errInvalidKey)
   842  
   843  	p[0].key = "foo"
   844  	assert.NoError(t, p.validate())
   845  
   846  	p = append(p, Property{key: "bar"})
   847  	assert.NoError(t, p.validate())
   848  }
   849  
   850  func TestMemberString(t *testing.T) {
   851  	// normal key value pair
   852  	member, _ := NewMember("key", "value")
   853  	memberStr := member.String()
   854  	assert.Equal(t, memberStr, "key=value")
   855  	// encoded key
   856  	member, _ = NewMember("key", "%3B")
   857  	memberStr = member.String()
   858  	assert.Equal(t, memberStr, "key=%3B")
   859  }
   860  
   861  var benchBaggage Baggage
   862  
   863  func BenchmarkNew(b *testing.B) {
   864  	mem1, _ := NewMember("key1", "val1")
   865  	mem2, _ := NewMember("key2", "val2")
   866  	mem3, _ := NewMember("key3", "val3")
   867  	mem4, _ := NewMember("key4", "val4")
   868  
   869  	b.ReportAllocs()
   870  	b.ResetTimer()
   871  
   872  	for i := 0; i < b.N; i++ {
   873  		benchBaggage, _ = New(mem1, mem2, mem3, mem4)
   874  	}
   875  }
   876  
   877  var benchMember Member
   878  
   879  func BenchmarkNewMember(b *testing.B) {
   880  	b.ReportAllocs()
   881  
   882  	for i := 0; i < b.N; i++ {
   883  		benchMember, _ = NewMember("key", "value")
   884  	}
   885  }
   886  
   887  func BenchmarkParse(b *testing.B) {
   888  	b.ReportAllocs()
   889  
   890  	for i := 0; i < b.N; i++ {
   891  		benchBaggage, _ = Parse(`userId=alice,serverNode = DF28 , isProduction = false,hasProp=stuff;propKey;propWValue=value`)
   892  	}
   893  }
   894  

View as plain text