...

Text file src/go.opentelemetry.io/otel/internal/shared/otlp/envconfig/envconfig_test.go.tmpl

Documentation: go.opentelemetry.io/otel/internal/shared/otlp/envconfig

     1// Code created by gotmpl. DO NOT MODIFY.
     2// source: internal/shared/otlp/envconfig/envconfig_test.go.tmpl
     3
     4// Copyright The OpenTelemetry Authors
     5//
     6// Licensed under the Apache License, Version 2.0 (the "License");
     7// you may not use this file except in compliance with the License.
     8// You may obtain a copy of the License at
     9//
    10//     http://www.apache.org/licenses/LICENSE-2.0
    11//
    12// Unless required by applicable law or agreed to in writing, software
    13// distributed under the License is distributed on an "AS IS" BASIS,
    14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15// See the License for the specific language governing permissions and
    16// limitations under the License.
    17
    18package envconfig
    19
    20import (
    21	"crypto/tls"
    22	"crypto/x509"
    23	"errors"
    24	"net/url"
    25	"testing"
    26	"time"
    27
    28	"github.com/stretchr/testify/assert"
    29)
    30
    31const WeakKey = `
    32-----BEGIN EC PRIVATE KEY-----
    33MHcCAQEEIEbrSPmnlSOXvVzxCyv+VR3a0HDeUTvOcqrdssZ2k4gFoAoGCCqGSM49
    34AwEHoUQDQgAEDMTfv75J315C3K9faptS9iythKOMEeV/Eep73nWX531YAkmmwBSB
    352dXRD/brsgLnfG57WEpxZuY7dPRbxu33BA==
    36-----END EC PRIVATE KEY-----
    37`
    38
    39const WeakCertificate = `
    40-----BEGIN CERTIFICATE-----
    41MIIBjjCCATWgAwIBAgIUKQSMC66MUw+kPp954ZYOcyKAQDswCgYIKoZIzj0EAwIw
    42EjEQMA4GA1UECgwHb3RlbC1nbzAeFw0yMjEwMTkwMDA5MTlaFw0yMzEwMTkwMDA5
    43MTlaMBIxEDAOBgNVBAoMB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
    44AAQMxN+/vknfXkLcr19qm1L2LK2Eo4wR5X8R6nvedZfnfVgCSabAFIHZ1dEP9uuy
    45Aud8bntYSnFm5jt09FvG7fcEo2kwZzAdBgNVHQ4EFgQUicGuhnTTkYLZwofXMNLK
    46SHFeCWgwHwYDVR0jBBgwFoAUicGuhnTTkYLZwofXMNLKSHFeCWgwDwYDVR0TAQH/
    47BAUwAwEB/zAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAwRAIg
    48Lfma8FnnxeSOi6223AsFfYwsNZ2RderNsQrS0PjEHb0CIBkrWacqARUAu7uT4cGu
    49jVcIxYQqhId5L8p/mAv2PWZS
    50-----END CERTIFICATE-----
    51`
    52
    53type testOption struct {
    54	TestString   string
    55	TestBool     bool
    56	TestDuration time.Duration
    57	TestHeaders  map[string]string
    58	TestURL      *url.URL
    59	TestTLS      *tls.Config
    60}
    61
    62func TestEnvConfig(t *testing.T) {
    63	parsedURL, err := url.Parse("https://example.com")
    64	assert.NoError(t, err)
    65
    66	options := []testOption{}
    67	for _, testcase := range []struct {
    68		name            string
    69		reader          EnvOptionsReader
    70		configs         []ConfigFn
    71		expectedOptions []testOption
    72	}{
    73		{
    74			name: "with no namespace and a matching key",
    75			reader: EnvOptionsReader{
    76				GetEnv: func(n string) string {
    77					if n == "HELLO" {
    78						return "world"
    79					}
    80					return ""
    81				},
    82			},
    83			configs: []ConfigFn{
    84				WithString("HELLO", func(v string) {
    85					options = append(options, testOption{TestString: v})
    86				}),
    87			},
    88			expectedOptions: []testOption{
    89				{
    90					TestString: "world",
    91				},
    92			},
    93		},
    94		{
    95			name: "with no namespace and a non-matching key",
    96			reader: EnvOptionsReader{
    97				GetEnv: func(n string) string {
    98					if n == "HELLO" {
    99						return "world"
   100					}
   101					return ""
   102				},
   103			},
   104			configs: []ConfigFn{
   105				WithString("HOLA", func(v string) {
   106					options = append(options, testOption{TestString: v})
   107				}),
   108			},
   109			expectedOptions: []testOption{},
   110		},
   111		{
   112			name: "with a namespace and a matching key",
   113			reader: EnvOptionsReader{
   114				Namespace: "MY_NAMESPACE",
   115				GetEnv: func(n string) string {
   116					if n == "MY_NAMESPACE_HELLO" {
   117						return "world"
   118					}
   119					return ""
   120				},
   121			},
   122			configs: []ConfigFn{
   123				WithString("HELLO", func(v string) {
   124					options = append(options, testOption{TestString: v})
   125				}),
   126			},
   127			expectedOptions: []testOption{
   128				{
   129					TestString: "world",
   130				},
   131			},
   132		},
   133		{
   134			name: "with no namespace and a non-matching key",
   135			reader: EnvOptionsReader{
   136				Namespace: "MY_NAMESPACE",
   137				GetEnv: func(n string) string {
   138					if n == "HELLO" {
   139						return "world"
   140					}
   141					return ""
   142				},
   143			},
   144			configs: []ConfigFn{
   145				WithString("HELLO", func(v string) {
   146					options = append(options, testOption{TestString: v})
   147				}),
   148			},
   149			expectedOptions: []testOption{},
   150		},
   151		{
   152			name: "with a bool config",
   153			reader: EnvOptionsReader{
   154				GetEnv: func(n string) string {
   155					if n == "HELLO" {
   156						return "true"
   157					} else if n == "WORLD" {
   158						return "false"
   159					}
   160					return ""
   161				},
   162			},
   163			configs: []ConfigFn{
   164				WithBool("HELLO", func(b bool) {
   165					options = append(options, testOption{TestBool: b})
   166				}),
   167				WithBool("WORLD", func(b bool) {
   168					options = append(options, testOption{TestBool: b})
   169				}),
   170			},
   171			expectedOptions: []testOption{
   172				{
   173					TestBool: true,
   174				},
   175				{
   176					TestBool: false,
   177				},
   178			},
   179		},
   180		{
   181			name: "with an invalid bool config",
   182			reader: EnvOptionsReader{
   183				GetEnv: func(n string) string {
   184					if n == "HELLO" {
   185						return "world"
   186					}
   187					return ""
   188				},
   189			},
   190			configs: []ConfigFn{
   191				WithBool("HELLO", func(b bool) {
   192					options = append(options, testOption{TestBool: b})
   193				}),
   194			},
   195			expectedOptions: []testOption{
   196				{
   197					TestBool: false,
   198				},
   199			},
   200		},
   201		{
   202			name: "with a duration config",
   203			reader: EnvOptionsReader{
   204				GetEnv: func(n string) string {
   205					if n == "HELLO" {
   206						return "60"
   207					}
   208					return ""
   209				},
   210			},
   211			configs: []ConfigFn{
   212				WithDuration("HELLO", func(v time.Duration) {
   213					options = append(options, testOption{TestDuration: v})
   214				}),
   215			},
   216			expectedOptions: []testOption{
   217				{
   218					TestDuration: 60_000_000, // 60 milliseconds
   219				},
   220			},
   221		},
   222		{
   223			name: "with an invalid duration config",
   224			reader: EnvOptionsReader{
   225				GetEnv: func(n string) string {
   226					if n == "HELLO" {
   227						return "world"
   228					}
   229					return ""
   230				},
   231			},
   232			configs: []ConfigFn{
   233				WithDuration("HELLO", func(v time.Duration) {
   234					options = append(options, testOption{TestDuration: v})
   235				}),
   236			},
   237			expectedOptions: []testOption{},
   238		},
   239		{
   240			name: "with headers",
   241			reader: EnvOptionsReader{
   242				GetEnv: func(n string) string {
   243					if n == "HELLO" {
   244						return "userId=42,userName=alice"
   245					}
   246					return ""
   247				},
   248			},
   249			configs: []ConfigFn{
   250				WithHeaders("HELLO", func(v map[string]string) {
   251					options = append(options, testOption{TestHeaders: v})
   252				}),
   253			},
   254			expectedOptions: []testOption{
   255				{
   256					TestHeaders: map[string]string{
   257						"userId":   "42",
   258						"userName": "alice",
   259					},
   260				},
   261			},
   262		},
   263		{
   264			name: "with invalid headers",
   265			reader: EnvOptionsReader{
   266				GetEnv: func(n string) string {
   267					if n == "HELLO" {
   268						return "world"
   269					}
   270					return ""
   271				},
   272			},
   273			configs: []ConfigFn{
   274				WithHeaders("HELLO", func(v map[string]string) {
   275					options = append(options, testOption{TestHeaders: v})
   276				}),
   277			},
   278			expectedOptions: []testOption{
   279				{
   280					TestHeaders: map[string]string{},
   281				},
   282			},
   283		},
   284		{
   285			name: "with URL",
   286			reader: EnvOptionsReader{
   287				GetEnv: func(n string) string {
   288					if n == "HELLO" {
   289						return "https://example.com"
   290					}
   291					return ""
   292				},
   293			},
   294			configs: []ConfigFn{
   295				WithURL("HELLO", func(v *url.URL) {
   296					options = append(options, testOption{TestURL: v})
   297				}),
   298			},
   299			expectedOptions: []testOption{
   300				{
   301					TestURL: parsedURL,
   302				},
   303			},
   304		},
   305		{
   306			name: "with invalid URL",
   307			reader: EnvOptionsReader{
   308				GetEnv: func(n string) string {
   309					if n == "HELLO" {
   310						return "i nvalid://url"
   311					}
   312					return ""
   313				},
   314			},
   315			configs: []ConfigFn{
   316				WithURL("HELLO", func(v *url.URL) {
   317					options = append(options, testOption{TestURL: v})
   318				}),
   319			},
   320			expectedOptions: []testOption{},
   321		},
   322	} {
   323		t.Run(testcase.name, func(t *testing.T) {
   324			testcase.reader.Apply(testcase.configs...)
   325			assert.Equal(t, testcase.expectedOptions, options)
   326			options = []testOption{}
   327		})
   328	}
   329}
   330
   331func TestWithTLSConfig(t *testing.T) {
   332	pool, err := createCertPool([]byte(WeakCertificate))
   333	assert.NoError(t, err)
   334
   335	reader := EnvOptionsReader{
   336		GetEnv: func(n string) string {
   337			if n == "CERTIFICATE" {
   338				return "/path/cert.pem"
   339			}
   340			return ""
   341		},
   342		ReadFile: func(p string) ([]byte, error) {
   343			if p == "/path/cert.pem" {
   344				return []byte(WeakCertificate), nil
   345			}
   346			return []byte{}, nil
   347		},
   348	}
   349
   350	var option testOption
   351	reader.Apply(
   352		WithCertPool("CERTIFICATE", func(cp *x509.CertPool) {
   353			option = testOption{TestTLS: &tls.Config{RootCAs: cp}}
   354		}),
   355	)
   356
   357	// nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool.
   358	assert.Equal(t, pool.Subjects(), option.TestTLS.RootCAs.Subjects())
   359}
   360
   361func TestWithClientCert(t *testing.T) {
   362	cert, err := tls.X509KeyPair([]byte(WeakCertificate), []byte(WeakKey))
   363	assert.NoError(t, err)
   364
   365	reader := EnvOptionsReader{
   366		GetEnv: func(n string) string {
   367			switch n {
   368			case "CLIENT_CERTIFICATE":
   369				return "/path/tls.crt"
   370			case "CLIENT_KEY":
   371				return "/path/tls.key"
   372			}
   373			return ""
   374		},
   375		ReadFile: func(n string) ([]byte, error) {
   376			switch n {
   377			case "/path/tls.crt":
   378				return []byte(WeakCertificate), nil
   379			case "/path/tls.key":
   380				return []byte(WeakKey), nil
   381			}
   382			return []byte{}, nil
   383		},
   384	}
   385
   386	var option testOption
   387	reader.Apply(
   388		WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) {
   389			option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}}
   390		}),
   391	)
   392	assert.Equal(t, cert, option.TestTLS.Certificates[0])
   393
   394	reader.ReadFile = func(s string) ([]byte, error) { return nil, errors.New("oops") }
   395	option.TestTLS = nil
   396	reader.Apply(
   397		WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) {
   398			option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}}
   399		}),
   400	)
   401	assert.Nil(t, option.TestTLS)
   402
   403	reader.GetEnv = func(s string) string { return "" }
   404	option.TestTLS = nil
   405	reader.Apply(
   406		WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) {
   407			option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}}
   408		}),
   409	)
   410	assert.Nil(t, option.TestTLS)
   411}
   412
   413func TestStringToHeader(t *testing.T) {
   414	tests := []struct {
   415		name  string
   416		value string
   417		want  map[string]string
   418	}{
   419		{
   420			name:  "simple test",
   421			value: "userId=alice",
   422			want:  map[string]string{"userId": "alice"},
   423		},
   424		{
   425			name:  "simple test with spaces",
   426			value: " userId = alice  ",
   427			want:  map[string]string{"userId": "alice"},
   428		},
   429		{
   430			name:  "simple header conforms to RFC 3986 spec",
   431			value: " userId = alice+test ",
   432			want:  map[string]string{"userId": "alice+test"},
   433		},
   434		{
   435			name:  "multiple headers encoded",
   436			value: "userId=alice,serverNode=DF%3A28,isProduction=false",
   437			want: map[string]string{
   438				"userId":       "alice",
   439				"serverNode":   "DF:28",
   440				"isProduction": "false",
   441			},
   442		},
   443		{
   444			name:  "multiple headers encoded per RFC 3986 spec",
   445			value: "userId=alice+test,serverNode=DF%3A28,isProduction=false,namespace=localhost/test",
   446			want: map[string]string{
   447				"userId":       "alice+test",
   448				"serverNode":   "DF:28",
   449				"isProduction": "false",
   450				"namespace":    "localhost/test",
   451			},
   452		},
   453		{
   454			name:  "invalid headers format",
   455			value: "userId:alice",
   456			want:  map[string]string{},
   457		},
   458		{
   459			name:  "invalid key",
   460			value: "%XX=missing,userId=alice",
   461			want: map[string]string{
   462				"userId": "alice",
   463			},
   464		},
   465		{
   466			name:  "invalid value",
   467			value: "missing=%XX,userId=alice",
   468			want: map[string]string{
   469				"userId": "alice",
   470			},
   471		},
   472	}
   473
   474	for _, tt := range tests {
   475		t.Run(tt.name, func(t *testing.T) {
   476			assert.Equal(t, tt.want, stringToHeader(tt.value))
   477		})
   478	}
   479}

View as plain text