/*
 * Copyright 2019, OpenTelemetry Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.opentelemetry.extensions.trace.propagation;

import static com.google.common.truth.Truth.assertThat;

import io.grpc.Context;
import io.opentelemetry.context.propagation.HttpTextFormat.Getter;
import io.opentelemetry.context.propagation.HttpTextFormat.Setter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.SpanId;
import io.opentelemetry.trace.TraceFlags;
import io.opentelemetry.trace.TraceId;
import io.opentelemetry.trace.TraceState;
import io.opentelemetry.trace.TracingContextUtils;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Unit tests for {@link io.opentelemetry.trace.propagation.HttpTraceContext}. */
@RunWith(JUnit4.class)
public class B3PropagatorTest {

  private static final TraceState TRACE_STATE_DEFAULT = TraceState.builder().build();
  private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041";
  private static final TraceId TRACE_ID = TraceId.fromLowerBase16(TRACE_ID_BASE16, 0);
  private static final String SHORT_TRACE_ID_BASE16 = "ff00000000000000";
  private static final TraceId SHORT_TRACE_ID =
      TraceId.fromLowerBase16(StringUtils.padLeft(SHORT_TRACE_ID_BASE16, 32), 0);
  private static final String SPAN_ID_BASE16 = "ff00000000000041";
  private static final SpanId SPAN_ID = SpanId.fromLowerBase16(SPAN_ID_BASE16, 0);
  private static final byte SAMPLED_TRACE_OPTIONS_BYTES = 1;
  private static final TraceFlags SAMPLED_TRACE_OPTIONS =
      TraceFlags.fromByte(SAMPLED_TRACE_OPTIONS_BYTES);
  private static final Setter<Map<String, String>> setter = Map::put;
  private static final Getter<Map<String, String>> getter =
      new Getter<Map<String, String>>() {
        @Nullable
        @Override
        public String get(Map<String, String> carrier, String key) {
          return carrier.get(key);
        }
      };
  private final B3Propagator b3Propagator = B3Propagator.getMultipleHeaderPropagator();
  private final B3Propagator b3PropagatorSingleHeader = B3Propagator.getSingleHeaderPropagator();
  @Rule public ExpectedException thrown = ExpectedException.none();

  private static SpanContext getSpanContext(Context context) {
    return TracingContextUtils.getSpan(context).getContext();
  }

  private static Context withSpanContext(SpanContext spanContext, Context context) {
    return TracingContextUtils.withSpan(DefaultSpan.create(spanContext), context);
  }

  @Test
  public void inject_invalidContext() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3Propagator.inject(
        withSpanContext(
            SpanContext.create(
                TraceId.getInvalid(),
                SpanId.getInvalid(),
                SAMPLED_TRACE_OPTIONS,
                TraceState.builder().set("foo", "bar").build()),
            Context.current()),
        carrier,
        setter);
    assertThat(carrier).hasSize(0);
  }

  @Test
  public void inject_SampledContext() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3Propagator.inject(
        withSpanContext(
            SpanContext.create(TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT),
            Context.current()),
        carrier,
        setter);
    assertThat(carrier).containsEntry(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SAMPLED_HEADER, "1");
  }

  @Test
  public void inject_SampledContext_nullCarrierUsage() {
    final Map<String, String> carrier = new LinkedHashMap<>();
    b3Propagator.inject(
        withSpanContext(
            SpanContext.create(TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT),
            Context.current()),
        null,
        (Setter<Map<String, String>>) (ignored, key, value) -> carrier.put(key, value));
    assertThat(carrier).containsEntry(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SAMPLED_HEADER, "1");
  }

  @Test
  public void inject_NotSampledContext() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3Propagator.inject(
        withSpanContext(
            SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT),
            Context.current()),
        carrier,
        setter);
    assertThat(carrier).containsEntry(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    assertThat(carrier).containsEntry(B3Propagator.SAMPLED_HEADER, "0");
  }

  @Test
  public void extract_SampledContext_Int() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, "true");

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_NotSampledContext() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, B3Propagator.FALSE_INT);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Int_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, SHORT_TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, SHORT_TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, "true");

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_NotSampledContext_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(B3Propagator.TRACE_ID_HEADER, SHORT_TRACE_ID_BASE16);
    carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    carrier.put(B3Propagator.SAMPLED_HEADER, B3Propagator.FALSE_INT);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_InvalidTraceId() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.TRACE_ID_HEADER, "abcdefghijklmnopabcdefghijklmnop");
    invalidHeaders.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    invalidHeaders.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);
    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidTraceId_Size() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16 + "00");
    invalidHeaders.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID_BASE16);
    invalidHeaders.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);
    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidSpanId() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    invalidHeaders.put(B3Propagator.SPAN_ID_HEADER, "abcdefghijklmnop");
    invalidHeaders.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);
    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidSpanId_Size() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID_BASE16);
    invalidHeaders.put(B3Propagator.SPAN_ID_HEADER, "abcdefghijklmnop" + "00");
    invalidHeaders.put(B3Propagator.SAMPLED_HEADER, B3Propagator.TRUE_INT);
    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void inject_invalidContext_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3PropagatorSingleHeader.inject(
        withSpanContext(
            SpanContext.create(
                TraceId.getInvalid(),
                SpanId.getInvalid(),
                SAMPLED_TRACE_OPTIONS,
                TraceState.builder().set("foo", "bar").build()),
            Context.current()),
        carrier,
        setter);
    assertThat(carrier).hasSize(0);
  }

  @Test
  public void inject_SampledContext_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3PropagatorSingleHeader.inject(
        withSpanContext(
            SpanContext.create(TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT),
            Context.current()),
        carrier,
        setter);

    assertThat(carrier)
        .containsEntry(
            B3Propagator.COMBINED_HEADER, TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "1");
  }

  @Test
  public void inject_NotSampledContext_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    b3PropagatorSingleHeader.inject(
        withSpanContext(
            SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT),
            Context.current()),
        carrier,
        setter);

    assertThat(carrier)
        .containsEntry(
            B3Propagator.COMBINED_HEADER, TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "0");
  }

  @Test
  public void extract_SampledContext_Int_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT);

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_DebugFlag_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT + "-" + "0");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER, TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "true");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool_DebugFlag_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "true" + "-" + "0");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_NotSampledContext_SingleHeader() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.FALSE_INT);

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Int_SingleHeader_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        SHORT_TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT);

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_DebugFlag_SingleHeader_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        SHORT_TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT + "-" + "0");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool_SingleHeader_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER, SHORT_TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "true");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_SampledContext_Bool_DebugFlag_SingleHeader_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        SHORT_TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + "true" + "-" + "0");

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_NotSampledContext_SingleHeader_Short_TraceId() {
    Map<String, String> carrier = new LinkedHashMap<>();
    carrier.put(
        B3Propagator.COMBINED_HEADER,
        SHORT_TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.FALSE_INT);

    assertThat(getSpanContext(b3PropagatorSingleHeader.extract(Context.current(), carrier, getter)))
        .isEqualTo(
            SpanContext.createFromRemoteParent(
                SHORT_TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TRACE_STATE_DEFAULT));
  }

  @Test
  public void extract_Null_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.COMBINED_HEADER, null);

    assertThat(
            getSpanContext(
                b3PropagatorSingleHeader.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_Empty_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.COMBINED_HEADER, "");

    assertThat(
            getSpanContext(
                b3PropagatorSingleHeader.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidTraceId_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(
        B3Propagator.COMBINED_HEADER,
        "abcdefghijklmnopabcdefghijklmnop" + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT);

    assertThat(
            getSpanContext(
                b3PropagatorSingleHeader.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidTraceId_Size_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(
        B3Propagator.COMBINED_HEADER,
        "abcdefghijklmnopabcdefghijklmnop"
            + "00"
            + "-"
            + SPAN_ID_BASE16
            + "-"
            + B3Propagator.TRUE_INT);

    assertThat(
            getSpanContext(
                b3PropagatorSingleHeader.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidSpanId_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + "abcdefghijklmnop" + "-" + B3Propagator.TRUE_INT);

    assertThat(
            getSpanContext(
                b3PropagatorSingleHeader.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_InvalidSpanId_Size_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + "abcdefghijklmnop" + "00" + "-" + B3Propagator.TRUE_INT);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_TooFewParts_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(B3Propagator.COMBINED_HEADER, TRACE_ID_BASE16);

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void extract_TooManyParts_SingleHeader() {
    Map<String, String> invalidHeaders = new LinkedHashMap<>();
    invalidHeaders.put(
        B3Propagator.COMBINED_HEADER,
        TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-" + B3Propagator.TRUE_INT + "-extra");

    assertThat(getSpanContext(b3Propagator.extract(Context.current(), invalidHeaders, getter)))
        .isSameInstanceAs(SpanContext.getInvalid());
  }

  @Test
  public void fieldsList() {
    assertThat(b3Propagator.fields())
        .containsExactly(
            B3Propagator.TRACE_ID_HEADER, B3Propagator.SPAN_ID_HEADER, B3Propagator.SAMPLED_HEADER);
  }

  @Test
  public void headerNames() {
    assertThat(B3Propagator.TRACE_ID_HEADER).isEqualTo("X-B3-TraceId");
    assertThat(B3Propagator.SPAN_ID_HEADER).isEqualTo("X-B3-SpanId");
    assertThat(B3Propagator.SAMPLED_HEADER).isEqualTo("X-B3-Sampled");
  }

  @Test
  public void extract_emptyCarrier() {
    Map<String, String> emptyHeaders = new HashMap<>();
    assertThat(getSpanContext(b3Propagator.extract(Context.current(), emptyHeaders, getter)))
        .isEqualTo(SpanContext.getInvalid());
  }
}