From ca205b8573ee2271c95213230fcc112b6dd74acd Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Mon, 24 Aug 2026 22:19:32 +0930 Subject: [PATCH 1/3] Support the bktec OTLP relay in Ruby Use bktec's dedicated local relay token for OpenTelemetry exports in both otel_enabled and otel_only modes. Keep the analytics token separate so otel_enabled can continue authenticating its normal JSON uploads. Document the relayed and direct-upload credential behavior and cover both OpenTelemetry modes in the collector configuration specs. Amp-Thread-ID: https://ampcode.com/threads/T-01a033cd-e302-7589-bcea-50c27c533600 Co-authored-by: Amp --- test-collector-ruby/CHANGELOG.md | 4 ++++ test-collector-ruby/README.md | 9 ++++--- test-collector-ruby/docs/opentelemetry.md | 11 +++++---- .../lib/buildkite/test_collector.rb | 2 +- .../spec/test_collector_spec.rb | 24 +++++++++++++++++++ 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/test-collector-ruby/CHANGELOG.md b/test-collector-ruby/CHANGELOG.md index 70a98883..dfaf9627 100644 --- a/test-collector-ruby/CHANGELOG.md +++ b/test-collector-ruby/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Use bktec's `BUILDKITE_TESTS_OTLP_TOKEN` for `otel_enabled` and `otel_only` + exports when the local OTLP relay is enabled, without replacing the token + used by normal JSON uploads. + * Align `otel_enabled` and `otel_only` on the same execution spans and run resources. `otel_only` now differs by adding `buildkite.execution.via=otlp` while skipping legacy trace capture and JSON result uploads. diff --git a/test-collector-ruby/README.md b/test-collector-ruby/README.md index 68e5ba01..bc912176 100644 --- a/test-collector-ruby/README.md +++ b/test-collector-ruby/README.md @@ -131,9 +131,12 @@ regardless of who owns the provider. In suite-owned mode, a supported Export needs Ruby 3.3 or newer, which is what the OpenTelemetry gems require. On older Rubies the option is accepted and does nothing. -Spans need `BUILDKITE_ANALYTICS_TOKEN` to be an agent OIDC token with the -`write_uploads` scope, from `buildkite-agent oidc request-token`. A suite API -token still uploads executions, but its spans are rejected. +When bktec's OTLP relay is enabled, the collector automatically sends spans to +its loopback endpoint using the injected `BUILDKITE_TESTS_OTLP_TOKEN`. In +`otel_enabled` mode, `BUILDKITE_ANALYTICS_TOKEN` remains unchanged for normal +JSON uploads. Without the relay, spans use `BUILDKITE_ANALYTICS_TOKEN`, which +must be an agent OIDC token with the `write_uploads` scope; a suite API token +still uploads executions, but its spans are rejected. Export failures never fail a test or block the normal Test Engine upload. See the [OpenTelemetry guide](docs/opentelemetry.md) for what you get and how it diff --git a/test-collector-ruby/docs/opentelemetry.md b/test-collector-ruby/docs/opentelemetry.md index edd926ba..b8ca5f9f 100644 --- a/test-collector-ruby/docs/opentelemetry.md +++ b/test-collector-ruby/docs/opentelemetry.md @@ -230,10 +230,13 @@ instrumentation unchanged. A warning reports an `[]` selection that was ignored. ## What gets sent -Spans go to Buildkite over OTLP, using the same `BUILDKITE_ANALYTICS_TOKEN` as -the rest of the collector. Sending them needs an agent OIDC token with the -`write_uploads` scope; a suite API token uploads test results as normal but its -spans are rejected. +When bktec's OTLP relay is enabled, spans go to its loopback endpoint using the +injected `BUILDKITE_TESTS_OTLP_TOKEN`. bktec forwards them to Buildkite with its +OIDC credential. `BUILDKITE_ANALYTICS_TOKEN` remains available for the normal +JSON uploads in `otel_enabled` mode. Without the relay, spans go directly to +Buildkite using `BUILDKITE_ANALYTICS_TOKEN`, which must be an agent OIDC token +with the `write_uploads` scope; a suite API token uploads test results as normal +but its spans are rejected. OpenTelemetry's SDK owns batching, retries, and transport. `test.execution` spans have a reserved, faster-draining queue and exporter. Forwarded children diff --git a/test-collector-ruby/lib/buildkite/test_collector.rb b/test-collector-ruby/lib/buildkite/test_collector.rb index 4c3abf13..42af924c 100644 --- a/test-collector-ruby/lib/buildkite/test_collector.rb +++ b/test-collector-ruby/lib/buildkite/test_collector.rb @@ -88,7 +88,7 @@ def self.configure(hook:, token: nil, url: nil, tracing_enabled: true, artifact_ @otel_options = { # Undocumented, for development purposes. endpoint: ENV["BUILDKITE_ANALYTICS_OTLP_ENDPOINT"] || Buildkite::TestCollector::OTel::DEFAULT_ENDPOINT, - api_token: api_token, + api_token: (ENV["BUILDKITE_TESTS_OTLP_TOKEN"] || api_token)&.strip, run_env: Buildkite::TestCollector::CI.env, instrumentations: otel_instrumentations, # Tags describe the whole run, so they ride along as resource diff --git a/test-collector-ruby/spec/test_collector_spec.rb b/test-collector-ruby/spec/test_collector_spec.rb index 9a12bb26..d4d05990 100644 --- a/test-collector-ruby/spec/test_collector_spec.rb +++ b/test-collector-ruby/spec/test_collector_spec.rb @@ -79,6 +79,30 @@ ) end + it "uses the bktec relay token in both OpenTelemetry modes without replacing the upload token" do + configured_tokens = [] + allow(Buildkite::TestCollector).to receive(:hook_into) + allow(Buildkite::TestCollector::OTel).to receive(:configure!) do |**options| + configured_tokens << options[:api_token] + end + allow(Buildkite::TestCollector::OTel).to receive(:enabled?) { true } + env_overlay["BUILDKITE_ANALYTICS_TOKEN"] = "upload-token" + env_overlay["BUILDKITE_TESTS_OTLP_TOKEN"] = " relay-token " + + [ + { otel_enabled: true }, + { otel_only: true }, + ].each do |mode| + Buildkite::TestCollector.configure(hook: hook, **mode) + expect(Buildkite::TestCollector.api_token).to eq("upload-token") + Buildkite::TestCollector.start_otel + end + + expect(configured_tokens).to eq(["relay-token", "relay-token"]) + ensure + Buildkite::TestCollector.otel_only = false + end + it "can override the endpoint for local development" do env_overlay["BUILDKITE_ANALYTICS_OTLP_ENDPOINT"] = "http://tests-otlp.buildkite.localhost/v1/traces" allow(Buildkite::TestCollector::CI).to receive(:env) { { "key" => "run-key" } } From f08c42f8ca9b1352c010ee9f40d93c5bd2f312aa Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Tue, 25 Aug 2026 09:37:19 +0930 Subject: [PATCH 2/3] Honor standard OTLP exporter headers Merge OTEL_EXPORTER_OTLP_TRACES_HEADERS, or its generic fallback, over the collector-generated OTLP headers using case-insensitive names. This lets bktec's standard Bearer authorization take precedence without replacing the analytics token used for normal JSON uploads. Keep environment-provided authorization authoritative when a warm worker reconfigures the collector with a refreshed analytics token. Remove the Ruby collector's dependency on the dedicated relay token variable and document the standard header behavior. Amp-Thread-ID: https://ampcode.com/threads/T-01a033cd-e302-7589-bcea-50c27c533600 Co-authored-by: Amp --- test-collector-ruby/CHANGELOG.md | 7 ++- test-collector-ruby/README.md | 14 +++-- test-collector-ruby/docs/opentelemetry.md | 13 ++-- .../lib/buildkite/test_collector.rb | 2 +- .../lib/buildkite/test_collector/otel.rb | 38 +++++++++++- .../spec/test_collector/otel_spec.rb | 59 ++++++++++++++++++- .../spec/test_collector_spec.rb | 24 -------- 7 files changed, 116 insertions(+), 41 deletions(-) diff --git a/test-collector-ruby/CHANGELOG.md b/test-collector-ruby/CHANGELOG.md index dfaf9627..d72f3a31 100644 --- a/test-collector-ruby/CHANGELOG.md +++ b/test-collector-ruby/CHANGELOG.md @@ -2,9 +2,10 @@ ## Unreleased -* Use bktec's `BUILDKITE_TESTS_OTLP_TOKEN` for `otel_enabled` and `otel_only` - exports when the local OTLP relay is enabled, without replacing the token - used by normal JSON uploads. +* Honor standard `OTEL_EXPORTER_OTLP_TRACES_HEADERS` and + `OTEL_EXPORTER_OTLP_HEADERS` in both OpenTelemetry modes. These headers take + precedence over collector-generated headers, allowing bktec's local OTLP + relay to authenticate exports without replacing the normal JSON upload token. * Align `otel_enabled` and `otel_only` on the same execution spans and run resources. `otel_only` now differs by adding `buildkite.execution.via=otlp` diff --git a/test-collector-ruby/README.md b/test-collector-ruby/README.md index bc912176..99a8276a 100644 --- a/test-collector-ruby/README.md +++ b/test-collector-ruby/README.md @@ -131,12 +131,14 @@ regardless of who owns the provider. In suite-owned mode, a supported Export needs Ruby 3.3 or newer, which is what the OpenTelemetry gems require. On older Rubies the option is accepted and does nothing. -When bktec's OTLP relay is enabled, the collector automatically sends spans to -its loopback endpoint using the injected `BUILDKITE_TESTS_OTLP_TOKEN`. In -`otel_enabled` mode, `BUILDKITE_ANALYTICS_TOKEN` remains unchanged for normal -JSON uploads. Without the relay, spans use `BUILDKITE_ANALYTICS_TOKEN`, which -must be an agent OIDC token with the `write_uploads` scope; a suite API token -still uploads executions, but its spans are rejected. +The collector honors standard `OTEL_EXPORTER_OTLP_TRACES_HEADERS` (or the +generic `OTEL_EXPORTER_OTLP_HEADERS`) and gives them precedence over its own +headers, including `Authorization`. bktec's OTLP relay uses this to provide its +local credential without changing `BUILDKITE_ANALYTICS_TOKEN`, which remains +available for normal JSON uploads in `otel_enabled` mode. Without an OTLP +Authorization header, spans use `BUILDKITE_ANALYTICS_TOKEN`, which must be an +agent OIDC token with the `write_uploads` scope; a suite API token still uploads +executions, but its spans are rejected. Export failures never fail a test or block the normal Test Engine upload. See the [OpenTelemetry guide](docs/opentelemetry.md) for what you get and how it diff --git a/test-collector-ruby/docs/opentelemetry.md b/test-collector-ruby/docs/opentelemetry.md index b8ca5f9f..f4a1b0da 100644 --- a/test-collector-ruby/docs/opentelemetry.md +++ b/test-collector-ruby/docs/opentelemetry.md @@ -230,10 +230,15 @@ instrumentation unchanged. A warning reports an `[]` selection that was ignored. ## What gets sent -When bktec's OTLP relay is enabled, spans go to its loopback endpoint using the -injected `BUILDKITE_TESTS_OTLP_TOKEN`. bktec forwards them to Buildkite with its -OIDC credential. `BUILDKITE_ANALYTICS_TOKEN` remains available for the normal -JSON uploads in `otel_enabled` mode. Without the relay, spans go directly to +The collector merges standard `OTEL_EXPORTER_OTLP_TRACES_HEADERS` (or, when it +is absent, `OTEL_EXPORTER_OTLP_HEADERS`) over its own OTLP headers. Header names +are matched case-insensitively, so a standard `authorization` entry takes +precedence over the credential sourced from `BUILDKITE_ANALYTICS_TOKEN`. + +bktec's OTLP relay uses the trace-specific header variable to provide its local +credential. bktec forwards spans to Buildkite with its OIDC credential while +`BUILDKITE_ANALYTICS_TOKEN` remains available for normal JSON uploads in +`otel_enabled` mode. Without an OTLP Authorization header, spans go directly to Buildkite using `BUILDKITE_ANALYTICS_TOKEN`, which must be an agent OIDC token with the `write_uploads` scope; a suite API token uploads test results as normal but its spans are rejected. diff --git a/test-collector-ruby/lib/buildkite/test_collector.rb b/test-collector-ruby/lib/buildkite/test_collector.rb index 42af924c..4c3abf13 100644 --- a/test-collector-ruby/lib/buildkite/test_collector.rb +++ b/test-collector-ruby/lib/buildkite/test_collector.rb @@ -88,7 +88,7 @@ def self.configure(hook:, token: nil, url: nil, tracing_enabled: true, artifact_ @otel_options = { # Undocumented, for development purposes. endpoint: ENV["BUILDKITE_ANALYTICS_OTLP_ENDPOINT"] || Buildkite::TestCollector::OTel::DEFAULT_ENDPOINT, - api_token: (ENV["BUILDKITE_TESTS_OTLP_TOKEN"] || api_token)&.strip, + api_token: api_token, run_env: Buildkite::TestCollector::CI.env, instrumentations: otel_instrumentations, # Tags describe the whole run, so they ride along as resource diff --git a/test-collector-ruby/lib/buildkite/test_collector/otel.rb b/test-collector-ruby/lib/buildkite/test_collector/otel.rb index e3c0fab0..9fe71e31 100644 --- a/test-collector-ruby/lib/buildkite/test_collector/otel.rb +++ b/test-collector-ruby/lib/buildkite/test_collector/otel.rb @@ -85,7 +85,13 @@ def configure!(endpoint: DEFAULT_ENDPOINT, api_token: nil, run_env: {}, instrume @api_token = api_token @run_key = run_env["key"] - headers = request_headers(run_env, api_token) + # Passing collector headers to the exporter bypasses its environment + # defaults, so merge the standard OTLP headers here instead. + environment_headers = otlp_headers_from_environment + @authorization_from_environment = environment_headers.keys.any? do |key| + key.casecmp?("Authorization") + end + headers = request_headers(run_env, api_token, environment_headers) # Run-level detail travels as the resource of the providers we create, # so every exported span carries it without repeating it per span. @@ -215,6 +221,7 @@ def shutdown @execution_child_forwarder = nil @exporters = nil @api_token = nil + @authorization_from_environment = nil @run_key = nil @tracer = nil end @@ -288,6 +295,10 @@ def refresh_authorization(api_token) return if api_token.nil? || api_token == @api_token @api_token = api_token + # Standard OTLP configuration remains authoritative across warm-worker + # reconfiguration, even when the collector receives a refreshed token. + return if @authorization_from_environment + value = authorization_header(api_token) refreshed = Array(@exporters).count do |exporter| headers = exporter.instance_variable_defined?(:@headers) && exporter.instance_variable_get(:@headers) @@ -511,12 +522,35 @@ def job_span_links [] end - def request_headers(run_env, api_token) + def request_headers(run_env, api_token, environment_headers = otlp_headers_from_environment) headers = { "Buildkite-Tests-Run-Key" => run_env["key"] } headers["Authorization"] = authorization_header(api_token) if api_token + environment_headers.each do |key, value| + headers.delete_if { |existing, _| existing.casecmp?(key) } + headers[key] = value + end headers end + def otlp_headers_from_environment + raw = ENV["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] + raw = ENV["OTEL_EXPORTER_OTLP_HEADERS"] if raw.nil? + return {} if raw.nil? + + entries = raw.split(",") + raise ArgumentError, "invalid OTLP exporter headers" if entries.empty? + + entries.each_with_object({}) do |entry, headers| + key, value = entry.split("=", 2).map { |part| URI.decode_uri_component(part) } + key = key.to_s.strip + value = value.to_s.strip + raise ArgumentError, "invalid OTLP exporter headers" if key.empty? || value.empty? + + headers.delete_if { |existing, _| existing.casecmp?(key) } + headers[key] = value + end + end + def authorization_header(api_token) "Token token=\"#{api_token}\"" end diff --git a/test-collector-ruby/spec/test_collector/otel_spec.rb b/test-collector-ruby/spec/test_collector/otel_spec.rb index 337273b4..50ca9372 100644 --- a/test-collector-ruby/spec/test_collector/otel_spec.rb +++ b/test-collector-ruby/spec/test_collector/otel_spec.rb @@ -528,6 +528,35 @@ def finish ) end + it "gives trace-specific OTLP headers precedence over generic and collector headers" do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS") + .and_return( + "authorization=Bearer%20relay-token,buildkite-tests-run-key=relay-run,x-extra=hello%20world" + ) + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_HEADERS") + .and_return("authorization=Bearer%20generic-token") + + headers = described_class.send(:request_headers, { "key" => "test-run-id" }, "suite-token") + + expect(headers).to eq( + "authorization" => "Bearer relay-token", + "buildkite-tests-run-key" => "relay-run", + "x-extra" => "hello world", + ) + end + + it "uses generic OTLP headers when trace-specific headers are absent" do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS").and_return(nil) + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_HEADERS") + .and_return("Authorization=Bearer%20generic-token") + + headers = described_class.send(:request_headers, { "key" => "test-run-id" }, "suite-token") + + expect(headers["Authorization"]).to eq("Bearer generic-token") + end + it "uses an AlwaysOn sampler, process-safe random IDs, and the run resource for execution roots" do processor = spy( "execution processor", @@ -734,7 +763,9 @@ def finish describe "token refresh" do def exporter_authorization_headers described_class.instance_variable_get(:@exporters).map do |exporter| - exporter.instance_variable_get(:@headers)["Authorization"] + exporter.instance_variable_get(:@headers).find do |key, _| + key.casecmp?("Authorization") + end&.last end end @@ -768,6 +799,32 @@ def exporter_authorization_headers OpenTelemetry.tracer_provider = original end + it "does not replace standard OTLP authorization when the collector token changes" do + original = OpenTelemetry.tracer_provider + suite_provider = OpenTelemetry::SDK::Trace::TracerProvider.new + OpenTelemetry.tracer_provider = suite_provider + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS") + .and_return("authorization=Bearer%20relay-token") + + described_class.configure!( + endpoint: "https://example.invalid/v1/traces", + api_token: "before-refresh", + run_env: { "key" => "run-123" }, + ) + described_class.configure!( + endpoint: "https://example.invalid/v1/traces", + api_token: "after-refresh", + run_env: { "key" => "run-123" }, + ) + + expect(exporter_authorization_headers).to eq(["Bearer relay-token"] * 2) + ensure + described_class.shutdown + suite_provider&.shutdown + OpenTelemetry.tracer_provider = original + end + it "warns when reconfigured with a different run key, keeping the original run" do original = OpenTelemetry.tracer_provider suite_provider = OpenTelemetry::SDK::Trace::TracerProvider.new diff --git a/test-collector-ruby/spec/test_collector_spec.rb b/test-collector-ruby/spec/test_collector_spec.rb index d4d05990..9a12bb26 100644 --- a/test-collector-ruby/spec/test_collector_spec.rb +++ b/test-collector-ruby/spec/test_collector_spec.rb @@ -79,30 +79,6 @@ ) end - it "uses the bktec relay token in both OpenTelemetry modes without replacing the upload token" do - configured_tokens = [] - allow(Buildkite::TestCollector).to receive(:hook_into) - allow(Buildkite::TestCollector::OTel).to receive(:configure!) do |**options| - configured_tokens << options[:api_token] - end - allow(Buildkite::TestCollector::OTel).to receive(:enabled?) { true } - env_overlay["BUILDKITE_ANALYTICS_TOKEN"] = "upload-token" - env_overlay["BUILDKITE_TESTS_OTLP_TOKEN"] = " relay-token " - - [ - { otel_enabled: true }, - { otel_only: true }, - ].each do |mode| - Buildkite::TestCollector.configure(hook: hook, **mode) - expect(Buildkite::TestCollector.api_token).to eq("upload-token") - Buildkite::TestCollector.start_otel - end - - expect(configured_tokens).to eq(["relay-token", "relay-token"]) - ensure - Buildkite::TestCollector.otel_only = false - end - it "can override the endpoint for local development" do env_overlay["BUILDKITE_ANALYTICS_OTLP_ENDPOINT"] = "http://tests-otlp.buildkite.localhost/v1/traces" allow(Buildkite::TestCollector::CI).to receive(:env) { { "key" => "run-key" } } From 3bfb981380e99729873e3d5d75920d46114b675a Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Tue, 25 Aug 2026 10:26:58 +0930 Subject: [PATCH 3/3] Treat empty OTLP header settings as unset Skip empty trace-specific and generic OTLP header environment values in accordance with the OpenTelemetry configuration specification. An empty trace-specific value now falls back to the generic setting, and two empty values leave the collector's normal run-key and authorization headers in place instead of disabling OpenTelemetry export. Amp-Thread-ID: https://ampcode.com/threads/T-01a033cd-e302-7589-bcea-50c27c533600 Co-authored-by: Amp --- test-collector-ruby/CHANGELOG.md | 1 + test-collector-ruby/docs/opentelemetry.md | 3 ++- .../lib/buildkite/test_collector/otel.rb | 4 ++-- .../spec/test_collector/otel_spec.rb | 17 +++++++++++++++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/test-collector-ruby/CHANGELOG.md b/test-collector-ruby/CHANGELOG.md index d72f3a31..68aa65c9 100644 --- a/test-collector-ruby/CHANGELOG.md +++ b/test-collector-ruby/CHANGELOG.md @@ -6,6 +6,7 @@ `OTEL_EXPORTER_OTLP_HEADERS` in both OpenTelemetry modes. These headers take precedence over collector-generated headers, allowing bktec's local OTLP relay to authenticate exports without replacing the normal JSON upload token. + Empty header environment variables are treated as unset. * Align `otel_enabled` and `otel_only` on the same execution spans and run resources. `otel_only` now differs by adding `buildkite.execution.via=otlp` diff --git a/test-collector-ruby/docs/opentelemetry.md b/test-collector-ruby/docs/opentelemetry.md index f4a1b0da..96ccabac 100644 --- a/test-collector-ruby/docs/opentelemetry.md +++ b/test-collector-ruby/docs/opentelemetry.md @@ -233,7 +233,8 @@ instrumentation unchanged. A warning reports an `[]` selection that was ignored. The collector merges standard `OTEL_EXPORTER_OTLP_TRACES_HEADERS` (or, when it is absent, `OTEL_EXPORTER_OTLP_HEADERS`) over its own OTLP headers. Header names are matched case-insensitively, so a standard `authorization` entry takes -precedence over the credential sourced from `BUILDKITE_ANALYTICS_TOKEN`. +precedence over the credential sourced from `BUILDKITE_ANALYTICS_TOKEN`. Empty +header environment variables are treated as unset. bktec's OTLP relay uses the trace-specific header variable to provide its local credential. bktec forwards spans to Buildkite with its OIDC credential while diff --git a/test-collector-ruby/lib/buildkite/test_collector/otel.rb b/test-collector-ruby/lib/buildkite/test_collector/otel.rb index 9fe71e31..5be6bf5c 100644 --- a/test-collector-ruby/lib/buildkite/test_collector/otel.rb +++ b/test-collector-ruby/lib/buildkite/test_collector/otel.rb @@ -534,8 +534,8 @@ def request_headers(run_env, api_token, environment_headers = otlp_headers_from_ def otlp_headers_from_environment raw = ENV["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] - raw = ENV["OTEL_EXPORTER_OTLP_HEADERS"] if raw.nil? - return {} if raw.nil? + raw = ENV["OTEL_EXPORTER_OTLP_HEADERS"] if raw.nil? || raw.empty? + return {} if raw.nil? || raw.empty? entries = raw.split(",") raise ArgumentError, "invalid OTLP exporter headers" if entries.empty? diff --git a/test-collector-ruby/spec/test_collector/otel_spec.rb b/test-collector-ruby/spec/test_collector/otel_spec.rb index 50ca9372..000ab7a3 100644 --- a/test-collector-ruby/spec/test_collector/otel_spec.rb +++ b/test-collector-ruby/spec/test_collector/otel_spec.rb @@ -546,9 +546,9 @@ def finish ) end - it "uses generic OTLP headers when trace-specific headers are absent" do + it "uses generic OTLP headers when trace-specific headers are empty" do allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS").and_return(nil) + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS").and_return("") allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_HEADERS") .and_return("Authorization=Bearer%20generic-token") @@ -557,6 +557,19 @@ def finish expect(headers["Authorization"]).to eq("Bearer generic-token") end + it "uses collector headers when both standard OTLP header variables are empty" do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_TRACES_HEADERS").and_return("") + allow(ENV).to receive(:[]).with("OTEL_EXPORTER_OTLP_HEADERS").and_return("") + + headers = described_class.send(:request_headers, { "key" => "test-run-id" }, "suite-token") + + expect(headers).to eq( + "Buildkite-Tests-Run-Key" => "test-run-id", + "Authorization" => %(Token token="suite-token"), + ) + end + it "uses an AlwaysOn sampler, process-safe random IDs, and the run resource for execution roots" do processor = spy( "execution processor",