opentelemetry-erlang-contrib/instrumentation/opentelemetry_httpoison/test/opentelemetry_httpoison_tes...

320 lines
12 KiB
Elixir

defmodule OpentelemetryHTTPoisonTest do
alias OpentelemetryHTTPoison
alias OpenTelemetry.Tracer
use OpentelemetryHTTPoison.Case, async: false
doctest OpentelemetryHTTPoison
require OpenTelemetry.Tracer
require Record
for {name, spec} <- Record.extract_all(from_lib: "opentelemetry/include/otel_span.hrl") do
Record.defrecord(name, spec)
end
setup do
flush_mailbox()
:otel_simple_processor.set_exporter(:otel_exporter_pid, self())
:ok
end
describe "OpentelemetryHTTPoison default attributes and headers" do
test "standard http client span attribute are set in span" do
OpentelemetryHTTPoison.get!("http://localhost:8000")
assert_receive {:span, span(attributes: attributes_record, name: "GET")}
attributes = elem(attributes_record, 4)
assert ["http.method", "http.status_code", "http.url", "net.peer.name"] ==
attributes |> Map.keys() |> Enum.sort()
assert {"http.method", "GET"} in attributes
assert {"net.peer.name", "localhost"} in attributes
end
test "traceparent header is injected when no headers" do
%HTTPoison.Response{request: %{headers: headers}} =
OpentelemetryHTTPoison.get!("http://localhost:8000")
assert "traceparent" in Enum.map(headers, &elem(&1, 0))
end
test "traceparent header is injected when list headers" do
%HTTPoison.Response{request: %{headers: headers}} =
OpentelemetryHTTPoison.get!("http://localhost:8000", [{"Accept", "application/json"}])
assert "traceparent" in Enum.map(headers, &elem(&1, 0))
end
test "traceparent header is injected to user-supplied map headers" do
%HTTPoison.Response{request: %{headers: headers}} =
OpentelemetryHTTPoison.get!("http://localhost:8000", %{"Accept" => "application/json"})
assert "traceparent" in Enum.map(headers, &elem(&1, 0))
end
test "traceparent header is injected to atom user-supplied map headers" do
%HTTPoison.Response{request: %{headers: headers}} =
OpentelemetryHTTPoison.get!("http://localhost:8000", %{atom: "value"})
assert "atom" in Enum.map(headers, &elem(&1, 0))
end
test "http.url doesn't contain credentials" do
OpentelemetryHTTPoison.get!("http://user:pass@localhost:8000/user/edit/24")
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"http.url", "http://localhost:8000/user/edit/24"})
end
end
describe "OpentelemetryHTTPoison calls with additional options" do
test "additional span attributes can be passed to OpentelemetryHTTPoison invocation" do
OpentelemetryHTTPoison.get!("http://localhost:8000", [], ot_attributes: [{"app.callname", "mariorossi"}])
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"app.callname", "mariorossi"})
end
test "resource route can be explicitly passed to OpentelemetryHTTPoison invocation as a string" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: "/user/edit")
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"http.route", "/user/edit"})
end
test "resource route can be explicitly passed to OpentelemetryHTTPoison invocation as a function" do
infer_fn = fn request -> URI.parse(request.url).path end
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: infer_fn)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"http.route", "/user/edit/24"})
end
test "resource route inference can be explicitly ignored" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: :ignore)
assert_receive {:span, span(attributes: attributes)}, 1000
refute confirm_http_route_attribute(attributes)
end
test "resource route inference can be implicitly ignored" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24")
assert_receive {:span, span(attributes: attributes)}, 1000
refute confirm_http_route_attribute(attributes)
end
test "resource route inference fails if an incorrect value is passed to the OpentelemetryHTTPoison invocation" do
assert_raise(ArgumentError, fn ->
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: nil)
end)
assert_raise(ArgumentError, fn ->
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: 1)
end)
end
test "resource route and attributes can be passed to OpentelemetryHTTPoison as options together" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [],
ot_resource_route: "/user/edit",
ot_attributes: [{"app.callname", "mariorossi"}]
)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"http.route", "/user/edit"})
assert confirm_attributes(attributes, {"app.callname", "mariorossi"})
end
end
describe "parent span is not affected" do
test "with a successful request" do
Tracer.with_span "parent" do
pre_request_ctx = Tracer.current_span_ctx()
OpentelemetryHTTPoison.get("http://localhost:8000")
post_request_ctx = Tracer.current_span_ctx()
assert post_request_ctx == pre_request_ctx
end
end
test "with an nxdomain request" do
Tracer.with_span "parent" do
pre_request_ctx = Tracer.current_span_ctx()
OpentelemetryHTTPoison.get("http://domain.invalid:8000")
post_request_ctx = Tracer.current_span_ctx()
assert post_request_ctx == pre_request_ctx
end
end
end
describe "span_status is set to error for" do
test "status codes >= 400" do
OpentelemetryHTTPoison.get!("http://localhost:8000/status/400")
assert_receive {:span, span(status: {:status, :error, ""})}
end
test "HTTP econnrefused errors" do
{:error, %HTTPoison.Error{reason: expected_reason}} =
OpentelemetryHTTPoison.get("http://localhost:8001")
assert_receive {:span, span(status: {:status, :error, recorded_reason})}
assert inspect(expected_reason) == recorded_reason
end
test "HTTP nxdomain errors" do
{:error, %HTTPoison.Error{reason: expected_reason}} =
OpentelemetryHTTPoison.get("http://domain.invalid:8001")
assert_receive {:span, span(status: {:status, :error, recorded_reason})}
assert inspect(expected_reason) == recorded_reason
end
test "HTTP tls errors" do
{:error, %HTTPoison.Error{reason: expected_reason}} =
OpentelemetryHTTPoison.get("https://localhost:8000")
assert_receive {:span, span(status: {:status, :error, recorded_reason})}
assert inspect(expected_reason) == recorded_reason
end
end
describe "OpentelemetryHTTPoison with additional configuration" do
test "default attributes can be set via a two element tuple list" do
set_env(:ot_attributes, [{"test_attribute", "test"}])
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24")
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"test_attribute", "test"})
end
test "default attributes that are not binary will be ignored" do
set_env(:ot_attributes, [
{"test_attribute", "test"},
{1, "ignored"},
{:ignored, "ignored_too"}
])
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24")
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"test_attribute", "test"})
end
test "default attributes can be overridden via a two element tuple list passed to the OpentelemetryHTTPoison invocation" do
set_env(:ot_attributes, [{"test_attribute", "test"}])
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [],
ot_attributes: [{"test_attribute", "overridden"}]
)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"test_attribute", "overridden"})
end
test "default attributes can be combined with attributes passed to the OpentelemetryHTTPoison invocation" do
set_env(:ot_attributes, [{"test_attribute", "test"}])
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [],
ot_attributes: [
{"another_test_attribute", "another test"},
{"test_attribute_overridden", "overridden"}
]
)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"test_attribute", "test"})
assert confirm_attributes(attributes, {"another_test_attribute", "another test"})
assert confirm_attributes(attributes, {"test_attribute_overridden", "overridden"})
end
test "resource route can be implicitly inferred by OpentelemetryHTTPoison invocation using a default function" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: :infer)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_http_route_attribute(attributes, "/user/:subpath")
end
test "resource route can be inferred by OpentelemetryHTTPoison invocation via a configured function" do
infer_fn = fn
%HTTPoison.Request{} = request -> URI.parse(request.url).path
end
set_env(:infer_route, infer_fn)
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: :infer)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_http_route_attribute(attributes, "/user/edit/24")
end
test "implicit resource route inference can be overridden with a function passed to the OpentelemetryHTTPoison invocation" do
infer_fn = fn
%HTTPoison.Request{} = request -> URI.parse(request.url).path
end
invocation_infer_fn = fn _ -> "test" end
set_env(:infer_route, infer_fn)
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: invocation_infer_fn)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_http_route_attribute(attributes, "test")
end
test "implicit resource route inference can be overridden with a string passed to the OpentelemetryHTTPoison invocation" do
infer_fn = fn
%HTTPoison.Request{} = request -> URI.parse(request.url).path
end
set_env(:infer_route, infer_fn)
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [], ot_resource_route: "test")
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_http_route_attribute(attributes, "test")
end
end
test "OpentelemetryHTTPoison works if setup is not called" do
OpentelemetryHTTPoison.get!("http://localhost:8000/user/edit/24", [],
ot_attributes: [{"some_attribute", "some value"}]
)
assert_receive {:span, span(attributes: attributes)}, 1000
assert confirm_attributes(attributes, {"some_attribute", "some value"})
end
def flush_mailbox do
receive do
_ -> flush_mailbox()
after
10 -> :ok
end
end
defp confirm_attributes(attributes, attributes_to_confirm) do
attributes
|> Tuple.to_list()
|> Enum.filter(&is_map/1)
|> Enum.any?(fn map ->
attributes_to_confirm in map
end)
end
defp confirm_http_route_attribute(attributes, value) do
confirm_attributes(attributes, {"http.route", value})
end
defp confirm_http_route_attribute(attributes) do
confirm_http_route_attribute(attributes, "")
end
end