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 15package jaeger 16 17import ( 18 "math" 19 "os" 20 "testing" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 25 "go.opentelemetry.io/otel/attribute" 26 ottest "go.opentelemetry.io/otel/internal/internaltest" 27) 28 29func Test_parseTags(t *testing.T) { 30 envStore, err := ottest.SetEnvVariables(map[string]string{ 31 "existing": "not-default", 32 }) 33 require.NoError(t, err) 34 defer func() { 35 require.NoError(t, envStore.Restore()) 36 }() 37 38 testCases := []struct { 39 name string 40 tagStr string 41 expectedTags []attribute.KeyValue 42 expectedError error 43 }{ 44 { 45 name: "string", 46 tagStr: "key=value", 47 expectedTags: []attribute.KeyValue{ 48 { 49 Key: "key", 50 Value: attribute.StringValue("value"), 51 }, 52 }, 53 }, 54 { 55 name: "int64", 56 tagStr: "k=9223372036854775807,k2=-9223372036854775808", 57 expectedTags: []attribute.KeyValue{ 58 { 59 Key: "k", 60 Value: attribute.Int64Value(math.MaxInt64), 61 }, 62 { 63 Key: "k2", 64 Value: attribute.Int64Value(math.MinInt64), 65 }, 66 }, 67 }, 68 { 69 name: "float64", 70 tagStr: "k=1.797693134862315708145274237317043567981e+308,k2=4.940656458412465441765687928682213723651e-324,k3=-1.2", 71 expectedTags: []attribute.KeyValue{ 72 { 73 Key: "k", 74 Value: attribute.Float64Value(math.MaxFloat64), 75 }, 76 { 77 Key: "k2", 78 Value: attribute.Float64Value(math.SmallestNonzeroFloat64), 79 }, 80 { 81 Key: "k3", 82 Value: attribute.Float64Value(-1.2), 83 }, 84 }, 85 }, 86 { 87 name: "multiple type values", 88 tagStr: "k=v,k2=123, k3=v3 ,k4=-1.2, k5=${existing:default},k6=${nonExisting:default}", 89 expectedTags: []attribute.KeyValue{ 90 { 91 Key: "k", 92 Value: attribute.StringValue("v"), 93 }, 94 { 95 Key: "k2", 96 Value: attribute.Int64Value(123), 97 }, 98 { 99 Key: "k3", 100 Value: attribute.StringValue("v3"), 101 }, 102 { 103 Key: "k4", 104 Value: attribute.Float64Value(-1.2), 105 }, 106 { 107 Key: "k5", 108 Value: attribute.StringValue("not-default"), 109 }, 110 { 111 Key: "k6", 112 Value: attribute.StringValue("default"), 113 }, 114 }, 115 }, 116 { 117 name: "malformed: only have key", 118 tagStr: "key", 119 expectedError: errTagValueNotFound, 120 }, 121 { 122 name: "malformed: environment key has no default value", 123 tagStr: "key=${foo}", 124 expectedError: errTagEnvironmentDefaultValueNotFound, 125 }, 126 } 127 128 for _, tc := range testCases { 129 t.Run(tc.name, func(t *testing.T) { 130 tags, err := parseTags(tc.tagStr) 131 if tc.expectedError == nil { 132 assert.NoError(t, err) 133 assert.Equal(t, tc.expectedTags, tags) 134 } else { 135 assert.Error(t, err) 136 assert.Equal(t, tc.expectedError, err) 137 assert.Equal(t, tc.expectedTags, tags) 138 } 139 }) 140 } 141} 142 143func Test_parseValue(t *testing.T) { 144 testCases := []struct { 145 name string 146 str string 147 expected attribute.Value 148 }{ 149 { 150 name: "bool: true", 151 str: "true", 152 expected: attribute.BoolValue(true), 153 }, 154 { 155 name: "bool: false", 156 str: "false", 157 expected: attribute.BoolValue(false), 158 }, 159 { 160 name: "int64: 012340", 161 str: "012340", 162 expected: attribute.Int64Value(12340), 163 }, 164 { 165 name: "int64: -012340", 166 str: "-012340", 167 expected: attribute.Int64Value(-12340), 168 }, 169 { 170 name: "int64: 0", 171 str: "0", 172 expected: attribute.Int64Value(0), 173 }, 174 { 175 name: "float64: -0.1", 176 str: "-0.1", 177 expected: attribute.Float64Value(-0.1), 178 }, 179 { 180 name: "float64: 00.001", 181 str: "00.001", 182 expected: attribute.Float64Value(0.001), 183 }, 184 { 185 name: "float64: 1E23", 186 str: "1E23", 187 expected: attribute.Float64Value(1e23), 188 }, 189 { 190 name: "string: foo", 191 str: "foo", 192 expected: attribute.StringValue("foo"), 193 }, 194 } 195 196 for _, tc := range testCases { 197 t.Run(tc.name, func(t *testing.T) { 198 v := parseValue(tc.str) 199 assert.Equal(t, tc.expected, v) 200 }) 201 } 202} 203 204func TestNewRawExporterWithEnv(t *testing.T) { 205 const ( 206 collectorEndpoint = "http://localhost" 207 username = "user" 208 password = "password" 209 serviceName = "test-service" 210 disabled = "false" 211 tags = "key=value" 212 ) 213 214 envStore, err := ottest.SetEnvVariables(map[string]string{ 215 envEndpoint: collectorEndpoint, 216 envUser: username, 217 envPassword: password, 218 envDisabled: disabled, 219 envServiceName: serviceName, 220 envTags: tags, 221 }) 222 require.NoError(t, err) 223 defer func() { 224 require.NoError(t, envStore.Restore()) 225 }() 226 227 // Create Jaeger Exporter with environment variables 228 exp, err := NewRawExporter( 229 WithCollectorEndpoint(CollectorEndpointFromEnv(), WithCollectorEndpointOptionFromEnv()), 230 WithDisabled(true), 231 WithDisabledFromEnv(), 232 WithProcessFromEnv(), 233 ) 234 235 assert.NoError(t, err) 236 assert.Equal(t, false, exp.o.Disabled) 237 assert.EqualValues(t, serviceName, exp.process.ServiceName) 238 assert.Len(t, exp.process.Tags, 1) 239 240 require.IsType(t, &collectorUploader{}, exp.uploader) 241 uploader := exp.uploader.(*collectorUploader) 242 assert.Equal(t, collectorEndpoint, uploader.endpoint) 243 assert.Equal(t, username, uploader.username) 244 assert.Equal(t, password, uploader.password) 245} 246 247func TestNewRawExporterWithEnvImplicitly(t *testing.T) { 248 const ( 249 collectorEndpoint = "http://localhost" 250 username = "user" 251 password = "password" 252 serviceName = "test-service" 253 disabled = "false" 254 tags = "key=value" 255 ) 256 257 envStore, err := ottest.SetEnvVariables(map[string]string{ 258 envEndpoint: collectorEndpoint, 259 envUser: username, 260 envPassword: password, 261 envDisabled: disabled, 262 envServiceName: serviceName, 263 envTags: tags, 264 }) 265 require.NoError(t, err) 266 defer func() { 267 require.NoError(t, envStore.Restore()) 268 }() 269 270 // Create Jaeger Exporter with environment variables 271 exp, err := NewRawExporter( 272 WithCollectorEndpoint("should be overwritten"), 273 WithDisabled(true), 274 ) 275 276 assert.NoError(t, err) 277 // NewRawExporter will ignore Disabled env 278 assert.Equal(t, true, exp.o.Disabled) 279 assert.EqualValues(t, serviceName, exp.process.ServiceName) 280 assert.Len(t, exp.process.Tags, 1) 281 282 require.IsType(t, &collectorUploader{}, exp.uploader) 283 uploader := exp.uploader.(*collectorUploader) 284 assert.Equal(t, collectorEndpoint, uploader.endpoint) 285 assert.Equal(t, username, uploader.username) 286 assert.Equal(t, password, uploader.password) 287} 288 289func TestCollectorEndpointFromEnv(t *testing.T) { 290 const ( 291 collectorEndpoint = "http://localhost" 292 ) 293 294 envStore, err := ottest.SetEnvVariables(map[string]string{ 295 envEndpoint: collectorEndpoint, 296 }) 297 require.NoError(t, err) 298 defer func() { 299 require.NoError(t, envStore.Restore()) 300 }() 301 302 assert.Equal(t, collectorEndpoint, CollectorEndpointFromEnv()) 303} 304 305func TestWithCollectorEndpointOptionFromEnv(t *testing.T) { 306 testCases := []struct { 307 name string 308 envUsername string 309 envPassword string 310 collectorEndpointOptions CollectorEndpointOptions 311 expectedCollectorEndpointOptions CollectorEndpointOptions 312 }{ 313 { 314 name: "overrides value via environment variables", 315 envUsername: "username", 316 envPassword: "password", 317 collectorEndpointOptions: CollectorEndpointOptions{ 318 username: "foo", 319 password: "bar", 320 }, 321 expectedCollectorEndpointOptions: CollectorEndpointOptions{ 322 username: "username", 323 password: "password", 324 }, 325 }, 326 { 327 name: "environment variables is empty, will not overwrite value", 328 envUsername: "", 329 envPassword: "", 330 collectorEndpointOptions: CollectorEndpointOptions{ 331 username: "foo", 332 password: "bar", 333 }, 334 expectedCollectorEndpointOptions: CollectorEndpointOptions{ 335 username: "foo", 336 password: "bar", 337 }, 338 }, 339 } 340 341 envStore := ottest.NewEnvStore() 342 envStore.Record(envUser) 343 envStore.Record(envPassword) 344 defer func() { 345 require.NoError(t, envStore.Restore()) 346 }() 347 for _, tc := range testCases { 348 t.Run(tc.name, func(t *testing.T) { 349 require.NoError(t, os.Setenv(envUser, tc.envUsername)) 350 require.NoError(t, os.Setenv(envPassword, tc.envPassword)) 351 352 f := WithCollectorEndpointOptionFromEnv() 353 f(&tc.collectorEndpointOptions) 354 355 assert.Equal(t, tc.expectedCollectorEndpointOptions, tc.collectorEndpointOptions) 356 }) 357 } 358} 359 360func TestWithDisabledFromEnv(t *testing.T) { 361 testCases := []struct { 362 name string 363 env string 364 options options 365 expectedOptions options 366 }{ 367 { 368 name: "overwriting", 369 env: "true", 370 options: options{}, 371 expectedOptions: options{Disabled: true}, 372 }, 373 { 374 name: "no overwriting", 375 env: "", 376 options: options{Disabled: true}, 377 expectedOptions: options{Disabled: true}, 378 }, 379 } 380 381 envStore := ottest.NewEnvStore() 382 envStore.Record(envDisabled) 383 defer func() { 384 require.NoError(t, envStore.Restore()) 385 }() 386 for _, tc := range testCases { 387 t.Run(tc.name, func(t *testing.T) { 388 require.NoError(t, os.Setenv(envDisabled, tc.env)) 389 390 f := WithDisabledFromEnv() 391 f(&tc.options) 392 393 assert.Equal(t, tc.expectedOptions, tc.options) 394 }) 395 } 396} 397 398func TestProcessFromEnv(t *testing.T) { 399 testCases := []struct { 400 name string 401 serviceName string 402 tags string 403 expectedProcess Process 404 }{ 405 { 406 name: "set process", 407 serviceName: "test-service", 408 tags: "key=value,key2=123", 409 expectedProcess: Process{ 410 ServiceName: "test-service", 411 Tags: []attribute.KeyValue{ 412 attribute.String("key", "value"), 413 attribute.Int64("key2", 123), 414 }, 415 }, 416 }, 417 { 418 name: "malformed tags", 419 serviceName: "test-service", 420 tags: "key", 421 expectedProcess: Process{ 422 ServiceName: "test-service", 423 }, 424 }, 425 } 426 427 for _, tc := range testCases { 428 t.Run(tc.name, func(t *testing.T) { 429 envStore, err := ottest.SetEnvVariables(map[string]string{ 430 envServiceName: tc.serviceName, 431 envTags: tc.tags, 432 }) 433 require.NoError(t, err) 434 435 p := ProcessFromEnv() 436 assert.Equal(t, tc.expectedProcess, p) 437 438 require.NoError(t, envStore.Restore()) 439 }) 440 } 441} 442 443func TestWithProcessFromEnv(t *testing.T) { 444 testCases := []struct { 445 name string 446 envServiceName string 447 envTags string 448 options options 449 expectedOptions options 450 }{ 451 { 452 name: "overwriting", 453 envServiceName: "service-name", 454 envTags: "key=value", 455 options: options{ 456 Process: Process{ 457 ServiceName: "old-name", 458 Tags: []attribute.KeyValue{ 459 attribute.String("old-key", "old-value"), 460 }, 461 }, 462 }, 463 expectedOptions: options{ 464 Process: Process{ 465 ServiceName: "service-name", 466 Tags: []attribute.KeyValue{ 467 attribute.String("key", "value"), 468 }, 469 }, 470 }, 471 }, 472 { 473 name: "no overwriting", 474 envServiceName: "", 475 envTags: "", 476 options: options{ 477 Process: Process{ 478 ServiceName: "old-name", 479 Tags: []attribute.KeyValue{ 480 attribute.String("old-key", "old-value"), 481 }, 482 }, 483 }, 484 expectedOptions: options{ 485 Process: Process{ 486 ServiceName: "old-name", 487 Tags: []attribute.KeyValue{ 488 attribute.String("old-key", "old-value"), 489 }, 490 }, 491 }, 492 }, 493 } 494 495 envStore := ottest.NewEnvStore() 496 envStore.Record(envServiceName) 497 envStore.Record(envTags) 498 defer func() { 499 require.NoError(t, envStore.Restore()) 500 }() 501 for _, tc := range testCases { 502 t.Run(tc.name, func(t *testing.T) { 503 require.NoError(t, os.Setenv(envServiceName, tc.envServiceName)) 504 require.NoError(t, os.Setenv(envTags, tc.envTags)) 505 506 f := WithProcessFromEnv() 507 f(&tc.options) 508 509 assert.Equal(t, tc.expectedOptions, tc.options) 510 }) 511 } 512} 513