use opentelemetry::trace::{SpanKind, TraceContextExt};
use opentelemetry_sdk::trace::{
InMemorySpanExporter, InMemorySpanExporterBuilder, SdkTracerProvider,
SimpleSpanProcessor, SpanData,
};
use serial_test::serial;
use std::sync::LazyLock;
static TEST_EXPORTER: LazyLock<InMemorySpanExporter> = LazyLock::new(|| {
let exporter = InMemorySpanExporterBuilder::new().build();
let provider = SdkTracerProvider::builder()
.with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
.build();
opentelemetry::global::set_tracer_provider(provider);
exporter
});
crate::tracer!();
crate::tracer!(custom);
struct TestHelper;
impl TestHelper {
fn new() -> Self {
LazyLock::force(&TEST_EXPORTER);
TEST_EXPORTER.reset();
Self
}
fn finished_spans(&self) -> Vec<SpanData> {
TEST_EXPORTER.get_finished_spans().unwrap_or_default()
}
fn find_span(&self, name: &str) -> Option<SpanData> {
self.finished_spans().into_iter().find(|s| s.name == name)
}
fn assert_span_exists(&self, name: &str) -> SpanData {
self.find_span(name).unwrap_or_else(|| {
let spans: Vec<_> = self
.finished_spans()
.iter()
.map(|s| s.name.to_string())
.collect();
panic!("Span '{}' not found. Available spans: {:?}", name, spans)
})
}
fn assert_span_has_attribute(
&self,
span: &SpanData,
key: &str,
expected: &str,
) {
let attr = span.attributes.iter().find(|kv| kv.key.as_str() == key);
match attr {
| Some(kv) => {
let value = format!("{}", kv.value);
assert_eq!(value, expected, "Attribute '{}' has wrong value", key);
},
| None => {
let keys: Vec<_> =
span.attributes.iter().map(|kv| kv.key.as_str()).collect();
panic!("Attribute '{}' not found. Available: {:?}", key, keys);
},
}
}
fn assert_span_has_event(&self, span: &SpanData, event_name: &str) {
let event = span.events.iter().find(|e| e.name == event_name);
assert!(
event.is_some(),
"Event '{}' not found. Available events: {:?}",
event_name,
span.events.iter().map(|e| &e.name).collect::<Vec<_>>()
);
}
fn assert_event_has_attribute(
&self,
span: &SpanData,
event_name: &str,
attr_key: &str,
expected: &str,
) {
let event = span
.events
.iter()
.find(|e| e.name == event_name)
.unwrap_or_else(|| panic!("Event '{}' not found", event_name));
let attr = event
.attributes
.iter()
.find(|kv| kv.key.as_str() == attr_key)
.unwrap_or_else(|| {
panic!(
"Event attribute '{}' not found in event '{}'",
attr_key, event_name
)
});
let value = format!("{}", attr.value);
assert_eq!(
value, expected,
"Event attribute '{}' has wrong value",
attr_key
);
}
fn assert_code_location_valid(&self, span: &SpanData) {
let ns = span
.attributes
.iter()
.find(|kv| kv.key.as_str() == "code.namespace")
.expect("code.namespace attribute missing");
let ns_value = format!("{}", ns.value);
assert!(
ns_value.ends_with("src/tests.rs"),
"code.namespace should end with 'src/tests.rs', got: {}",
ns_value
);
let lineno = span
.attributes
.iter()
.find(|kv| kv.key.as_str() == "code.lineno")
.expect("code.lineno attribute missing");
let lineno_value = format!("{}", lineno.value);
let lineno_int: i64 = lineno_value.parse().unwrap_or_else(|_| {
panic!("code.lineno should be an integer, got: {}", lineno_value)
});
assert!(
lineno_int > 0,
"code.lineno should be positive, got: {}",
lineno_int
);
let col = span
.attributes
.iter()
.find(|kv| kv.key.as_str() == "code.column")
.expect("code.column attribute missing");
let col_value = format!("{}", col.value);
let col_int: i64 = col_value.parse().unwrap_or_else(|_| {
panic!("code.column should be an integer, got: {}", col_value)
});
assert!(
col_int > 0,
"code.column should be positive, got: {}",
col_int
);
let thread = span
.attributes
.iter()
.find(|kv| kv.key.as_str() == "thread.name");
assert!(thread.is_some(), "thread.name attribute missing");
}
fn assert_span_timing_valid(&self, span: &SpanData) {
assert!(
span.start_time <= span.end_time,
"Span start_time ({:?}) should be <= end_time ({:?})",
span.start_time,
span.end_time
);
}
fn assert_link_matches(
&self,
span: &SpanData,
link_index: usize,
expected_trace_id: opentelemetry::trace::TraceId,
expected_span_id: opentelemetry::trace::SpanId,
) {
let link = span.links.iter().nth(link_index).unwrap_or_else(|| {
panic!(
"Link at index {} not found, span has {} links",
link_index,
span.links.len()
)
});
assert_eq!(
link.span_context.trace_id(),
expected_trace_id,
"Link trace_id mismatch at index {}",
link_index
);
assert_eq!(
link.span_context.span_id(),
expected_span_id,
"Link span_id mismatch at index {}",
link_index
);
}
fn assert_is_root_span(&self, span: &SpanData) {
assert!(
!span.parent_span_id.to_bytes().iter().any(|&b| b != 0),
"Span should be root (no parent), but has parent_span_id: {:?}",
span.parent_span_id
);
}
fn assert_span_has_i64_attribute(
&self,
span: &SpanData,
key: &str,
expected: i64,
) {
let attr = span.attributes.iter().find(|kv| kv.key.as_str() == key);
match attr {
| Some(kv) => match &kv.value {
| opentelemetry::Value::I64(v) => {
assert_eq!(*v, expected, "Attribute '{}' has wrong i64 value", key);
},
| other => {
panic!("Attribute '{}' should be I64 but was {:?}", key, other);
},
},
| None => {
let keys: Vec<_> =
span.attributes.iter().map(|kv| kv.key.as_str()).collect();
panic!("Attribute '{}' not found. Available: {:?}", key, keys);
},
}
}
fn assert_span_has_f64_attribute(
&self,
span: &SpanData,
key: &str,
expected: f64,
) {
let attr = span.attributes.iter().find(|kv| kv.key.as_str() == key);
match attr {
| Some(kv) => match &kv.value {
| opentelemetry::Value::F64(v) => {
assert!(
(*v - expected).abs() < f64::EPSILON * 100.0,
"Attribute '{}' has wrong f64 value: expected {}, got {}",
key,
expected,
v
);
},
| other => {
panic!("Attribute '{}' should be F64 but was {:?}", key, other);
},
},
| None => {
let keys: Vec<_> =
span.attributes.iter().map(|kv| kv.key.as_str()).collect();
panic!("Attribute '{}' not found. Available: {:?}", key, keys);
},
}
}
fn assert_span_has_bool_attribute(
&self,
span: &SpanData,
key: &str,
expected: bool,
) {
let attr = span.attributes.iter().find(|kv| kv.key.as_str() == key);
match attr {
| Some(kv) => match &kv.value {
| opentelemetry::Value::Bool(v) => {
assert_eq!(*v, expected, "Attribute '{}' has wrong bool value", key);
},
| other => {
panic!("Attribute '{}' should be Bool but was {:?}", key, other);
},
},
| None => {
let keys: Vec<_> =
span.attributes.iter().map(|kv| kv.key.as_str()).collect();
panic!("Attribute '{}' not found. Available: {:?}", key, keys);
},
}
}
fn assert_span_has_string_attribute(
&self,
span: &SpanData,
key: &str,
expected: &str,
) {
let attr = span.attributes.iter().find(|kv| kv.key.as_str() == key);
match attr {
| Some(kv) => match &kv.value {
| opentelemetry::Value::String(v) => {
assert_eq!(
v.as_str(),
expected,
"Attribute '{}' has wrong string value",
key
);
},
| other => {
panic!("Attribute '{}' should be String but was {:?}", key, other);
},
},
| None => {
let keys: Vec<_> =
span.attributes.iter().map(|kv| kv.key.as_str()).collect();
panic!("Attribute '{}' not found. Available: {:?}", key, keys);
},
}
}
fn assert_same_trace(&self, span1: &SpanData, span2: &SpanData) {
assert_eq!(
span1.span_context.trace_id(),
span2.span_context.trace_id(),
"Spans should be in the same trace"
);
}
fn assert_user_attribute_count(&self, span: &SpanData, expected: usize) {
let auto_attrs = [
"code.namespace",
"code.lineno",
"code.column",
"thread.name",
];
let user_count = span
.attributes
.iter()
.filter(|kv| !auto_attrs.contains(&kv.key.as_str()))
.count();
assert_eq!(
user_count,
expected,
"Expected {} user attributes, found {}. User attrs: {:?}",
expected,
user_count,
span
.attributes
.iter()
.filter(|kv| !auto_attrs.contains(&kv.key.as_str()))
.map(|kv| kv.key.as_str())
.collect::<Vec<_>>()
);
}
}
impl Drop for TestHelper {
fn drop(&mut self) {
TEST_EXPORTER.reset();
}
}
#[test]
#[serial]
fn statement_form_basic() {
let helper = TestHelper::new();
{
crate::span!("test.statement.basic");
}
let span = helper.assert_span_exists("test.statement.basic");
helper.assert_span_timing_valid(&span);
}
#[test]
#[serial]
fn statement_form_with_attributes() {
let helper = TestHelper::new();
{
crate::span!(
"test.statement.attrs",
"http.method" = "GET",
"http.status_code" = 200i64
);
}
let span = helper.assert_span_exists("test.statement.attrs");
helper.assert_span_has_attribute(&span, "http.method", "GET");
helper.assert_span_has_attribute(&span, "http.status_code", "200");
}
#[test]
#[serial]
fn statement_form_with_f64_attribute() {
let helper = TestHelper::new();
{
crate::span!(
"test.statement.f64",
"ratio" = 3.14159f64,
"temperature" = -40.5f64
);
}
let span = helper.assert_span_exists("test.statement.f64");
helper.assert_span_has_attribute(&span, "ratio", "3.14159");
helper.assert_span_has_attribute(&span, "temperature", "-40.5");
}
#[test]
#[serial]
fn statement_form_with_bool_attribute() {
let helper = TestHelper::new();
{
crate::span!("test.statement.bool", "enabled" = true, "debug" = false);
}
let span = helper.assert_span_exists("test.statement.bool");
helper.assert_span_has_attribute(&span, "enabled", "true");
helper.assert_span_has_attribute(&span, "debug", "false");
}
#[test]
#[serial]
fn statement_form_with_kind() {
let helper = TestHelper::new();
{
crate::span!("test.statement.server", kind = SpanKind::Server);
}
let span = helper.assert_span_exists("test.statement.server");
assert_eq!(span.span_kind, SpanKind::Server);
}
#[test]
#[serial]
fn statement_form_with_kind_and_attributes() {
let helper = TestHelper::new();
{
crate::span!(
"test.statement.kind_attrs",
kind = SpanKind::Client,
"db.system" = "postgresql"
);
}
let span = helper.assert_span_exists("test.statement.kind_attrs");
assert_eq!(span.span_kind, SpanKind::Client);
helper.assert_span_has_attribute(&span, "db.system", "postgresql");
}
#[test]
#[serial]
fn statement_form_includes_code_location() {
let helper = TestHelper::new();
{
crate::span!("test.statement.location");
}
let span = helper.assert_span_exists("test.statement.location");
helper.assert_code_location_valid(&span);
}
#[test]
#[serial]
fn statement_form_explicit_tracer() {
let helper = TestHelper::new();
{
crate::span!(@TRACER, "test.explicit.tracer");
}
helper.assert_span_exists("test.explicit.tracer");
}
#[test]
#[serial]
fn statement_form_explicit_tracer_with_attrs() {
let helper = TestHelper::new();
{
crate::span!(@TRACER, "test.explicit.attrs", "key" = "value");
}
let span = helper.assert_span_exists("test.explicit.attrs");
helper.assert_span_has_attribute(&span, "key", "value");
}
#[test]
#[serial]
fn statement_form_explicit_tracer_with_kind() {
let helper = TestHelper::new();
{
crate::span!(@TRACER, "test.explicit.kind", kind = SpanKind::Producer);
}
let span = helper.assert_span_exists("test.explicit.kind");
assert_eq!(span.span_kind, SpanKind::Producer);
}
#[test]
#[serial]
fn statement_form_named_tracer() {
let helper = TestHelper::new();
{
crate::span!(@CUSTOM_TRACER, "test.custom.tracer");
}
helper.assert_span_exists("test.custom.tracer");
}
#[test]
#[serial]
fn statement_form_explicit_tracer_context_none() {
let helper = TestHelper::new();
{
crate::span!("test.would_be_parent");
{
crate::span!(@TRACER, "test.explicit.root", context = None);
}
}
let span = helper.assert_span_exists("test.explicit.root");
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn detached_form_basic() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.detached.basic");
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.basic");
helper.assert_span_timing_valid(&span);
}
#[test]
#[serial]
fn detached_form_with_attributes() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.detached.attrs", "request.id" = "abc123");
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.attrs");
helper.assert_span_has_attribute(&span, "request.id", "abc123");
}
#[test]
#[serial]
fn detached_form_with_kind() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.detached.kind", kind = SpanKind::Consumer);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.kind");
assert_eq!(span.span_kind, SpanKind::Consumer);
}
#[test]
#[serial]
fn detached_form_explicit_tracer() {
let helper = TestHelper::new();
let cx = crate::span!(^ @TRACER, "test.detached.explicit");
let _attached = cx.attach();
drop(_attached);
helper.assert_span_exists("test.detached.explicit");
}
#[test]
#[serial]
fn detached_form_explicit_context() {
let helper = TestHelper::new();
let parent_cx = crate::span!(^ "test.detached.parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
let child_cx =
crate::span!(^ "test.detached.child", context = parent_cx.clone());
let _child = child_cx.attach();
drop(_child);
drop(_parent);
let child = helper.assert_span_exists("test.detached.child");
assert_eq!(child.parent_span_id, parent_span_id);
}
#[test]
#[serial]
fn closure_form_basic() {
let helper = TestHelper::new();
let result = crate::span!("test.closure.basic", in |_cx| {
42
});
assert_eq!(result, 42);
helper.assert_span_exists("test.closure.basic");
}
#[test]
#[serial]
fn closure_form_with_attributes() {
let helper = TestHelper::new();
let result = crate::span!("test.closure.attrs", "operation" = "compute", in |_cx| {
"done"
});
assert_eq!(result, "done");
let span = helper.assert_span_exists("test.closure.attrs");
helper.assert_span_has_attribute(&span, "operation", "compute");
}
#[test]
#[serial]
fn closure_form_with_kind() {
let helper = TestHelper::new();
crate::span!("test.closure.kind", kind = SpanKind::Internal, in |_cx| {});
let span = helper.assert_span_exists("test.closure.kind");
assert_eq!(span.span_kind, SpanKind::Internal);
}
#[test]
#[serial]
fn closure_form_with_kind_and_attributes() {
let helper = TestHelper::new();
crate::span!("test.closure.kind_attrs", kind = SpanKind::Server, "rpc.method" = "GetUser", in |_cx| {});
let span = helper.assert_span_exists("test.closure.kind_attrs");
assert_eq!(span.span_kind, SpanKind::Server);
helper.assert_span_has_attribute(&span, "rpc.method", "GetUser");
}
#[test]
#[serial]
fn closure_form_explicit_tracer() {
let helper = TestHelper::new();
crate::span!(@TRACER, "test.closure.explicit", in |_cx| {});
helper.assert_span_exists("test.closure.explicit");
}
#[test]
#[serial]
fn closure_form_captures_context() {
let helper = TestHelper::new();
let captured_trace_id = std::cell::Cell::new(None);
crate::span!("test.closure.context", in |cx| {
captured_trace_id.set(Some(cx.span().span_context().trace_id()));
});
let span = helper.assert_span_exists("test.closure.context");
assert_eq!(
captured_trace_id.get().unwrap(),
span.span_context.trace_id()
);
}
#[test]
#[serial]
fn closure_form_with_guard_capture() {
let helper = TestHelper::new();
let guard_captured = std::cell::Cell::new(false);
crate::span!("test.closure.guard", in |_cx, _guard| {
guard_captured.set(true);
});
assert!(guard_captured.get(), "Guard should be captured in closure");
helper.assert_span_exists("test.closure.guard");
}
#[test]
#[serial]
fn closure_form_context_none() {
let helper = TestHelper::new();
{
crate::span!("test.closure.would_be_parent");
let result = crate::span!("test.closure.root", context = None, in |_cx| {
"root_result"
});
assert_eq!(result, "root_result");
}
let span = helper.assert_span_exists("test.closure.root");
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn closure_form_context_none_with_attributes() {
let helper = TestHelper::new();
{
crate::span!("test.closure.root_attrs", context = None, "source" = "batch", in |_cx| {});
}
let span = helper.assert_span_exists("test.closure.root_attrs");
helper.assert_is_root_span(&span);
helper.assert_span_has_attribute(&span, "source", "batch");
}
#[test]
#[serial]
fn closure_form_context_none_with_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.closure.linked", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _g = linked_cx.attach();
drop(_g);
crate::span!("test.closure.with_link", context = None, links = [link], in |_cx| {});
let span = helper.assert_span_exists("test.closure.with_link");
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_form_explicit_context() {
let helper = TestHelper::new();
let parent_cx =
crate::span!(^ "test.closure.explicit_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!("test.closure.explicit_child", context = parent_cx.clone(), in |_cx| {});
drop(_parent);
let child = helper.assert_span_exists("test.closure.explicit_child");
assert_eq!(child.parent_span_id, parent_span_id);
}
#[test]
#[serial]
fn closure_form_explicit_context_with_attributes() {
let helper = TestHelper::new();
let parent_cx =
crate::span!(^ "test.closure.ctx_attr_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!("test.closure.ctx_attr_child", context = parent_cx.clone(), "child.attr" = "value", in |_cx| {});
drop(_parent);
let child = helper.assert_span_exists("test.closure.ctx_attr_child");
assert_eq!(child.parent_span_id, parent_span_id);
helper.assert_span_has_attribute(&child, "child.attr", "value");
}
#[test]
#[serial]
fn closure_form_explicit_tracer_context_none() {
let helper = TestHelper::new();
{
crate::span!("test.would_be_parent2");
crate::span!(@TRACER, "test.closure.tracer_root", context = None, in |_cx| {});
}
let span = helper.assert_span_exists("test.closure.tracer_root");
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn context_none_creates_root_span() {
let helper = TestHelper::new();
{
crate::span!("test.would_be_parent3");
{
crate::span!("test.new_root", context = None);
}
}
let root = helper.assert_span_exists("test.new_root");
helper.assert_is_root_span(&root);
}
#[test]
#[serial]
fn context_none_with_attributes() {
let helper = TestHelper::new();
{
crate::span!(
"test.root.attrs",
context = None,
"root.cause" = "user_request"
);
}
let span = helper.assert_span_exists("test.root.attrs");
helper.assert_span_has_attribute(&span, "root.cause", "user_request");
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn context_explicit_parent() {
let helper = TestHelper::new();
let parent_cx = crate::span!(^ "test.explicit_parent");
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent_guard = parent_cx.clone().attach();
{
crate::span!("test.explicit_child", context = parent_cx.clone());
}
drop(_parent_guard);
let child = helper.assert_span_exists("test.explicit_child");
assert_eq!(
child.parent_span_id, parent_span_id,
"Child should have explicit parent's span ID"
);
}
#[test]
#[serial]
fn detached_context_none() {
let helper = TestHelper::new();
{
crate::span!("test.detached.would_be_parent");
let cx = crate::span!(^ "test.detached.root", context = None);
let _attached = cx.attach();
}
let root = helper.assert_span_exists("test.detached.root");
helper.assert_is_root_span(&root);
}
#[test]
#[serial]
fn span_with_link_via_context_none() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.linked_span", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked_guard = linked_cx.attach();
drop(_linked_guard);
{
crate::span!("test.span_with_link", context = None, links = [link]);
}
let span = helper.assert_span_exists("test.span_with_link");
assert_eq!(span.links.len(), 1, "Span should have 1 link");
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn span_with_link_and_attributes() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.linked2", context = None);
let link = crate::link!(linked_cx.clone());
let _g = linked_cx.attach();
drop(_g);
{
crate::span!(
"test.link_with_attrs",
context = None,
links = [link],
"batch.id" = "batch-001"
);
}
let span = helper.assert_span_exists("test.link_with_attrs");
assert_eq!(span.links.len(), 1);
helper.assert_span_has_attribute(&span, "batch.id", "batch-001");
}
#[test]
#[serial]
fn detached_span_with_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.linked3", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _g = linked_cx.attach();
drop(_g);
let cx =
crate::span!(^ "test.detached.with_links", context = None, links = [link]);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.with_links");
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn span_with_multiple_links() {
let helper = TestHelper::new();
let linked_cx1 = crate::span!(^ "test.multi_link1", context = None);
let trace_id1 = linked_cx1.span().span_context().trace_id();
let span_id1 = linked_cx1.span().span_context().span_id();
let link1 = crate::link!(linked_cx1.clone());
let _g1 = linked_cx1.attach();
drop(_g1);
let linked_cx2 = crate::span!(^ "test.multi_link2", context = None);
let trace_id2 = linked_cx2.span().span_context().trace_id();
let span_id2 = linked_cx2.span().span_context().span_id();
let link2 = crate::link!(linked_cx2.clone());
let _g2 = linked_cx2.attach();
drop(_g2);
let linked_cx3 = crate::span!(^ "test.multi_link3", context = None);
let trace_id3 = linked_cx3.span().span_context().trace_id();
let span_id3 = linked_cx3.span().span_context().span_id();
let link3 = crate::link!(linked_cx3.clone());
let _g3 = linked_cx3.attach();
drop(_g3);
{
crate::span!(
"test.triple_linked",
context = None,
links = [link1, link2, link3]
);
}
let span = helper.assert_span_exists("test.triple_linked");
assert_eq!(span.links.len(), 3, "Span should have 3 links");
helper.assert_link_matches(&span, 0, trace_id1, span_id1);
helper.assert_link_matches(&span, 1, trace_id2, span_id2);
helper.assert_link_matches(&span, 2, trace_id3, span_id3);
}
#[test]
#[serial]
fn span_with_link_inherits_parent() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.link_target", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.link_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
{
crate::span!(
"test.link_child_with_parent",
context = parent_cx.clone(),
links = [link]
);
}
drop(_parent);
let child = helper.assert_span_exists("test.link_child_with_parent");
assert_eq!(child.parent_span_id, parent_span_id, "Should have parent");
assert_eq!(child.links.len(), 1, "Should have link");
helper.assert_link_matches(&child, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn nested_spans_automatic_parent_child() {
let helper = TestHelper::new();
{
crate::span!("test.parent");
{
crate::span!("test.child");
}
}
let parent = helper.assert_span_exists("test.parent");
let child = helper.assert_span_exists("test.child");
assert_eq!(
child.parent_span_id,
parent.span_context.span_id(),
"Child should automatically have parent's span ID"
);
}
#[test]
#[serial]
fn three_level_nesting() {
let helper = TestHelper::new();
{
crate::span!("test.grandparent");
{
crate::span!("test.parent_nested");
{
crate::span!("test.grandchild");
}
}
}
let grandparent = helper.assert_span_exists("test.grandparent");
let parent = helper.assert_span_exists("test.parent_nested");
let grandchild = helper.assert_span_exists("test.grandchild");
assert_eq!(parent.parent_span_id, grandparent.span_context.span_id());
assert_eq!(grandchild.parent_span_id, parent.span_context.span_id());
}
#[test]
#[serial]
fn span_kind_server() {
let helper = TestHelper::new();
{
crate::span!("test.kind.server", kind = SpanKind::Server);
}
assert_eq!(
helper.assert_span_exists("test.kind.server").span_kind,
SpanKind::Server
);
}
#[test]
#[serial]
fn span_kind_client() {
let helper = TestHelper::new();
{
crate::span!("test.kind.client", kind = SpanKind::Client);
}
assert_eq!(
helper.assert_span_exists("test.kind.client").span_kind,
SpanKind::Client
);
}
#[test]
#[serial]
fn span_kind_producer() {
let helper = TestHelper::new();
{
crate::span!("test.kind.producer", kind = SpanKind::Producer);
}
assert_eq!(
helper.assert_span_exists("test.kind.producer").span_kind,
SpanKind::Producer
);
}
#[test]
#[serial]
fn span_kind_consumer() {
let helper = TestHelper::new();
{
crate::span!("test.kind.consumer", kind = SpanKind::Consumer);
}
assert_eq!(
helper.assert_span_exists("test.kind.consumer").span_kind,
SpanKind::Consumer
);
}
#[test]
#[serial]
fn span_kind_internal() {
let helper = TestHelper::new();
{
crate::span!("test.kind.internal", kind = SpanKind::Internal);
}
assert_eq!(
helper.assert_span_exists("test.kind.internal").span_kind,
SpanKind::Internal
);
}
#[test]
#[serial]
fn event_simple() {
let helper = TestHelper::new();
{
crate::span!("test.event.parent");
crate::event!("my.event");
}
let span = helper.assert_span_exists("test.event.parent");
helper.assert_span_has_event(&span, "my.event");
}
#[test]
#[serial]
fn event_with_attributes() {
let helper = TestHelper::new();
{
crate::span!("test.event.attrs");
crate::event!("query.executed", "rows_affected" = 42i64, "table" = "users");
}
let span = helper.assert_span_exists("test.event.attrs");
helper.assert_span_has_event(&span, "query.executed");
helper.assert_event_has_attribute(
&span,
"query.executed",
"rows_affected",
"42",
);
helper.assert_event_has_attribute(&span, "query.executed", "table", "users");
}
#[test]
#[serial]
fn multiple_events_on_span() {
let helper = TestHelper::new();
{
crate::span!("test.multi.events");
crate::event!("step.one");
crate::event!("step.two");
crate::event!("step.three");
}
let span = helper.assert_span_exists("test.multi.events");
helper.assert_span_has_event(&span, "step.one");
helper.assert_span_has_event(&span, "step.two");
helper.assert_span_has_event(&span, "step.three");
assert_eq!(span.events.len(), 3);
}
#[test]
#[serial]
fn event_outside_span_context() {
let helper = TestHelper::new();
crate::event!("orphan.event", "key" = "value");
assert_eq!(helper.finished_spans().len(), 0);
}
#[test]
#[serial]
fn exception_records_event() {
let helper = TestHelper::new();
{
crate::span!("test.exception");
let _err = crate::exception!("io_error", "file not found");
}
let span = helper.assert_span_exists("test.exception");
helper.assert_span_has_event(&span, "exception");
helper.assert_event_has_attribute(
&span,
"exception",
"exception.type",
"io_error",
);
helper.assert_event_has_attribute(
&span,
"exception",
"exception.message",
"file not found",
);
}
#[test]
#[serial]
fn exception_returns_error_value() {
let helper = TestHelper::new();
let returned = {
crate::span!("test.exception.return", in |_cx| {
crate::exception!("custom_error", "my error message")
})
};
assert_eq!(returned, "my error message");
helper.assert_span_exists("test.exception.return");
}
#[test]
#[serial]
fn exception_with_additional_attributes() {
let helper = TestHelper::new();
{
crate::span!("test.exception.attrs");
let _err = crate::exception!(
"permission_error",
"access denied",
"path" = "/etc/passwd"
);
}
let span = helper.assert_span_exists("test.exception.attrs");
helper.assert_span_has_event(&span, "exception");
helper.assert_event_has_attribute(&span, "exception", "path", "/etc/passwd");
}
#[test]
#[serial]
fn exception_outside_span_context() {
let helper = TestHelper::new();
let err = crate::exception!("orphan_error", "orphan message");
assert_eq!(err, "orphan message");
assert_eq!(helper.finished_spans().len(), 0);
}
#[test]
#[serial]
fn error_records_event() {
let helper = TestHelper::new();
{
crate::span!("test.error");
let _err = crate::error!("timeout", "connection timed out");
}
let span = helper.assert_span_exists("test.error");
helper.assert_span_has_event(&span, "error");
helper.assert_event_has_attribute(&span, "error", "error.type", "timeout");
helper.assert_event_has_attribute(
&span,
"error",
"error.message",
"connection timed out",
);
}
#[test]
#[serial]
fn error_returns_error_value() {
let helper = TestHelper::new();
let returned = {
crate::span!("test.error.return", in |_cx| {
crate::error!("validation_error", "invalid input")
})
};
assert_eq!(returned, "invalid input");
helper.assert_span_exists("test.error.return");
}
#[test]
#[serial]
fn error_with_additional_attributes() {
let helper = TestHelper::new();
{
crate::span!("test.error.attrs");
let _err = crate::error!(
"connection_error",
"refused",
"host" = "localhost",
"port" = 8080i64
);
}
let span = helper.assert_span_exists("test.error.attrs");
helper.assert_span_has_event(&span, "error");
helper.assert_event_has_attribute(&span, "error", "host", "localhost");
helper.assert_event_has_attribute(&span, "error", "port", "8080");
}
#[test]
#[serial]
fn error_outside_span_context() {
let helper = TestHelper::new();
let err = crate::error!("orphan_error", "orphan error message");
assert_eq!(err, "orphan error message");
assert_eq!(helper.finished_spans().len(), 0);
}
#[test]
#[serial]
fn link_from_current_context() {
let helper = TestHelper::new();
let captured_link;
{
crate::span!("test.link.source");
captured_link = crate::link!();
}
let span = helper.assert_span_exists("test.link.source");
assert_eq!(
captured_link.span_context.trace_id(),
span.span_context.trace_id()
);
assert_eq!(
captured_link.span_context.span_id(),
span.span_context.span_id()
);
}
#[test]
#[serial]
fn link_from_explicit_context() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.link.explicit.source", context = None);
let expected_trace_id = cx.span().span_context().trace_id();
let expected_span_id = cx.span().span_context().span_id();
let link = crate::link!(cx.clone());
let _g = cx.attach();
drop(_g);
assert_eq!(link.span_context.trace_id(), expected_trace_id);
assert_eq!(link.span_context.span_id(), expected_span_id);
helper.assert_span_exists("test.link.explicit.source");
}
#[test]
#[serial]
fn link_with_attributes_from_current() {
let _helper = TestHelper::new();
{
crate::span!("test.link.attrs.source");
let link = crate::link!("link.type" = "spawned_from", "priority" = "high");
assert_eq!(link.attributes.len(), 2);
assert_eq!(link.attributes[0].key.as_str(), "link.type");
assert_eq!(format!("{}", link.attributes[0].value), "spawned_from");
assert_eq!(link.attributes[1].key.as_str(), "priority");
assert_eq!(format!("{}", link.attributes[1].value), "high");
}
}
#[test]
#[serial]
fn link_with_attributes_from_explicit_context() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.link.explicit.attrs", context = None);
let link = crate::link!(cx.clone(), "link.type" = "batch_trigger");
let _g = cx.attach();
drop(_g);
assert_eq!(link.attributes.len(), 1);
assert_eq!(link.attributes[0].key.as_str(), "link.type");
assert_eq!(format!("{}", link.attributes[0].value), "batch_trigger");
helper.assert_span_exists("test.link.explicit.attrs");
}
#[test]
#[serial]
fn default_tracer_produces_spans() {
let helper = TestHelper::new();
{
crate::span!("test.tracer.default");
}
helper.assert_span_exists("test.tracer.default");
}
#[test]
#[serial]
fn named_tracer_produces_spans() {
let helper = TestHelper::new();
{
crate::span!(@CUSTOM_TRACER, "test.tracer.custom");
}
helper.assert_span_exists("test.tracer.custom");
}
#[test]
#[serial]
fn default_span_kind_is_internal() {
let helper = TestHelper::new();
{
crate::span!("test.default.kind");
}
let span = helper.assert_span_exists("test.default.kind");
assert_eq!(
span.span_kind,
SpanKind::Internal,
"Default SpanKind should be Internal"
);
}
#[test]
#[serial]
fn detached_form_kind_with_attributes() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.detached.kind_attrs", kind = SpanKind::Producer, "queue" = "orders");
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.kind_attrs");
assert_eq!(span.span_kind, SpanKind::Producer);
helper.assert_span_has_attribute(&span, "queue", "orders");
}
#[test]
#[serial]
fn detached_form_explicit_tracer_with_kind() {
let helper = TestHelper::new();
let cx = crate::span!(^ @TRACER, "test.detached.tracer_kind", kind = SpanKind::Consumer);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.tracer_kind");
assert_eq!(span.span_kind, SpanKind::Consumer);
}
#[test]
#[serial]
fn detached_form_explicit_tracer_kind_attrs() {
let helper = TestHelper::new();
let cx = crate::span!(^ @TRACER, "test.detached.tracer_kind_attrs", kind = SpanKind::Client, "db.system" = "redis");
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.detached.tracer_kind_attrs");
assert_eq!(span.span_kind, SpanKind::Client);
helper.assert_span_has_attribute(&span, "db.system", "redis");
}
#[test]
#[serial]
fn detached_form_context_links_attrs() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.det_link_target", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.det_ctx_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
let cx = crate::span!(^ "test.detached.ctx_links_attrs", context = parent_cx.clone(), links = [link], "batch.id" = "b123");
let _child = cx.attach();
drop(_child);
drop(_parent);
let span = helper.assert_span_exists("test.detached.ctx_links_attrs");
assert_eq!(
span.parent_span_id, parent_span_id,
"Should have explicit parent"
);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
helper.assert_span_has_attribute(&span, "batch.id", "b123");
}
#[test]
#[serial]
fn closure_form_explicit_tracer_explicit_context() {
let helper = TestHelper::new();
let parent_cx =
crate::span!(^ "test.closure.tracer_ctx_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!(@TRACER, "test.closure.tracer_ctx_child", context = parent_cx.clone(), in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.closure.tracer_ctx_child");
assert_eq!(span.parent_span_id, parent_span_id);
}
#[test]
#[serial]
fn closure_form_explicit_tracer_context_attrs() {
let helper = TestHelper::new();
let parent_cx =
crate::span!(^ "test.closure.tracer_ctx_attr_p", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!(@TRACER, "test.closure.tracer_ctx_attrs", context = parent_cx.clone(), "key" = "val", in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.closure.tracer_ctx_attrs");
assert_eq!(span.parent_span_id, parent_span_id);
helper.assert_span_has_attribute(&span, "key", "val");
}
#[test]
#[serial]
fn closure_form_explicit_context_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.closure_link_target", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx =
crate::span!(^ "test.closure_ctx_link_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!("test.closure.ctx_links", context = parent_cx.clone(), links = [link], in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.closure.ctx_links");
assert_eq!(span.parent_span_id, parent_span_id);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_form_explicit_context_links_attrs() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.closure_lnk_attr_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.closure_ctx_lnk_attr_p", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!("test.closure.ctx_links_attrs", context = parent_cx.clone(), links = [link], "op" = "process", in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.closure.ctx_links_attrs");
assert_eq!(span.parent_span_id, parent_span_id);
assert_eq!(span.links.len(), 1);
helper.assert_span_has_attribute(&span, "op", "process");
}
#[test]
#[serial]
fn empty_links_array() {
let helper = TestHelper::new();
{
crate::span!("test.empty_links", context = None, links = []);
}
let span = helper.assert_span_exists("test.empty_links");
assert_eq!(span.links.len(), 0, "Should have no links");
}
#[test]
#[serial]
fn trace_id_consistency_nested() {
let helper = TestHelper::new();
{
crate::span!("test.trace_parent");
{
crate::span!("test.trace_child");
{
crate::span!("test.trace_grandchild");
}
}
}
let parent = helper.assert_span_exists("test.trace_parent");
let child = helper.assert_span_exists("test.trace_child");
let grandchild = helper.assert_span_exists("test.trace_grandchild");
helper.assert_same_trace(&parent, &child);
helper.assert_same_trace(&child, &grandchild);
helper.assert_same_trace(&parent, &grandchild);
}
#[test]
#[serial]
fn typed_attribute_i64() {
let helper = TestHelper::new();
{
crate::span!("test.typed.i64", "count" = 42i64, "negative" = -100i64);
}
let span = helper.assert_span_exists("test.typed.i64");
helper.assert_span_has_i64_attribute(&span, "count", 42);
helper.assert_span_has_i64_attribute(&span, "negative", -100);
}
#[test]
#[serial]
fn typed_attribute_f64() {
let helper = TestHelper::new();
{
crate::span!("test.typed.f64", "pi" = 3.14159f64, "neg" = -2.5f64);
}
let span = helper.assert_span_exists("test.typed.f64");
helper.assert_span_has_f64_attribute(&span, "pi", 3.14159);
helper.assert_span_has_f64_attribute(&span, "neg", -2.5);
}
#[test]
#[serial]
fn typed_attribute_bool() {
let helper = TestHelper::new();
{
crate::span!("test.typed.bool", "enabled" = true, "debug" = false);
}
let span = helper.assert_span_exists("test.typed.bool");
helper.assert_span_has_bool_attribute(&span, "enabled", true);
helper.assert_span_has_bool_attribute(&span, "debug", false);
}
#[test]
#[serial]
fn typed_attribute_string() {
let helper = TestHelper::new();
{
crate::span!("test.typed.string", "method" = "GET", "path" = "/api/users");
}
let span = helper.assert_span_exists("test.typed.string");
helper.assert_span_has_string_attribute(&span, "method", "GET");
helper.assert_span_has_string_attribute(&span, "path", "/api/users");
}
#[test]
#[serial]
fn mixed_attribute_types() {
let helper = TestHelper::new();
{
crate::span!(
"test.mixed.attrs",
"string_attr" = "hello",
"int_attr" = 42i64,
"float_attr" = 3.14f64,
"bool_attr" = true
);
}
let span = helper.assert_span_exists("test.mixed.attrs");
helper.assert_span_has_string_attribute(&span, "string_attr", "hello");
helper.assert_span_has_i64_attribute(&span, "int_attr", 42);
helper.assert_span_has_f64_attribute(&span, "float_attr", 3.14);
helper.assert_span_has_bool_attribute(&span, "bool_attr", true);
helper.assert_user_attribute_count(&span, 4);
}
#[test]
#[serial]
fn trailing_comma_basic() {
let helper = TestHelper::new();
{
crate::span!("test.trailing.comma",);
}
helper.assert_span_exists("test.trailing.comma");
}
#[test]
#[serial]
fn trailing_comma_with_attrs() {
let helper = TestHelper::new();
{
crate::span!("test.trailing.comma.attrs", "key" = "value",);
}
let span = helper.assert_span_exists("test.trailing.comma.attrs");
helper.assert_span_has_attribute(&span, "key", "value");
}
#[test]
#[serial]
fn user_attribute_count() {
let helper = TestHelper::new();
{
crate::span!("test.attr.count", "a" = "1", "b" = "2", "c" = "3");
}
let span = helper.assert_span_exists("test.attr.count");
helper.assert_user_attribute_count(&span, 3);
}
#[test]
#[serial]
fn explicit_tracer_context_none_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.exp_ctx_none_link_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
{
crate::span!(@TRACER, "test.exp_ctx_none_links", context = None, links = [link]);
}
let span = helper.assert_span_exists("test.exp_ctx_none_links");
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn explicit_tracer_explicit_context_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.exp_exp_link_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.exp_exp_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
{
crate::span!(@TRACER, "test.exp_ctx_exp_links", context = parent_cx.clone(), links = [link]);
}
drop(_parent);
let span = helper.assert_span_exists("test.exp_ctx_exp_links");
assert_eq!(span.parent_span_id, parent_span_id);
assert_eq!(span.links.len(), 1);
}
#[test]
#[serial]
fn detached_explicit_tracer_context_none_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.det_exp_none_link_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let cx = crate::span!(^ @TRACER, "test.det_exp_ctx_none_links", context = None, links = [link]);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.det_exp_ctx_none_links");
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_explicit_tracer_context_none_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.clo_exp_none_link_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
crate::span!(@TRACER, "test.clo_exp_ctx_none_links", context = None, links = [link], in |_cx| {});
let span = helper.assert_span_exists("test.clo_exp_ctx_none_links");
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_explicit_tracer_explicit_context_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.clo_exp_exp_link_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.clo_exp_exp_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!(@TRACER, "test.clo_exp_ctx_exp_links", context = parent_cx.clone(), links = [link], in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.clo_exp_ctx_exp_links");
assert_eq!(span.parent_span_id, parent_span_id);
assert_eq!(span.links.len(), 1);
}
#[test]
#[serial]
fn statement_kind_context_none() {
let helper = TestHelper::new();
{
crate::span!(
"test.stmt_kind_ctx_none",
kind = SpanKind::Server,
context = None
);
}
let span = helper.assert_span_exists("test.stmt_kind_ctx_none");
assert_eq!(span.span_kind, SpanKind::Server);
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn statement_kind_explicit_context() {
let helper = TestHelper::new();
let parent_cx = crate::span!(^ "test.stmt_kind_ctx_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
{
crate::span!(
"test.stmt_kind_ctx_exp",
kind = SpanKind::Client,
context = parent_cx.clone()
);
}
drop(_parent);
let span = helper.assert_span_exists("test.stmt_kind_ctx_exp");
assert_eq!(span.span_kind, SpanKind::Client);
assert_eq!(span.parent_span_id, parent_span_id);
}
#[test]
#[serial]
fn statement_kind_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.stmt_kind_links_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
{
crate::span!(
"test.stmt_kind_links",
kind = SpanKind::Producer,
links = [link]
);
}
let span = helper.assert_span_exists("test.stmt_kind_links");
assert_eq!(span.span_kind, SpanKind::Producer);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn statement_kind_context_links() {
let helper = TestHelper::new();
let linked_cx =
crate::span!(^ "test.stmt_kind_ctx_links_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
{
crate::span!(
"test.stmt_kind_ctx_links",
kind = SpanKind::Consumer,
context = None,
links = [link]
);
}
let span = helper.assert_span_exists("test.stmt_kind_ctx_links");
assert_eq!(span.span_kind, SpanKind::Consumer);
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
}
#[test]
#[serial]
fn statement_kind_context_links_attrs() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.stmt_full_combo_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
{
crate::span!(
"test.stmt_full_combo",
kind = SpanKind::Server,
context = None,
links = [link],
"op" = "process"
);
}
let span = helper.assert_span_exists("test.stmt_full_combo");
assert_eq!(span.span_kind, SpanKind::Server);
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_span_has_attribute(&span, "op", "process");
}
#[test]
#[serial]
fn detached_kind_context_none() {
let helper = TestHelper::new();
let cx = crate::span!(^ "test.det_kind_ctx_none", kind = SpanKind::Client, context = None);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.det_kind_ctx_none");
assert_eq!(span.span_kind, SpanKind::Client);
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn detached_kind_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.det_kind_links_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let cx = crate::span!(^ "test.det_kind_links", kind = SpanKind::Producer, links = [link]);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.det_kind_links");
assert_eq!(span.span_kind, SpanKind::Producer);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn detached_kind_context_links_attrs() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.det_full_combo_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let cx = crate::span!(^ "test.det_full_combo", kind = SpanKind::Consumer, context = None, links = [link], "batch" = "yes");
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.det_full_combo");
assert_eq!(span.span_kind, SpanKind::Consumer);
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_span_has_attribute(&span, "batch", "yes");
}
#[test]
#[serial]
fn closure_kind_context_none() {
let helper = TestHelper::new();
crate::span!("test.clo_kind_ctx_none", kind = SpanKind::Server, context = None, in |_cx| {});
let span = helper.assert_span_exists("test.clo_kind_ctx_none");
assert_eq!(span.span_kind, SpanKind::Server);
helper.assert_is_root_span(&span);
}
#[test]
#[serial]
fn closure_kind_explicit_context() {
let helper = TestHelper::new();
let parent_cx = crate::span!(^ "test.clo_kind_ctx_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.clone().attach();
crate::span!("test.clo_kind_ctx_exp", kind = SpanKind::Client, context = parent_cx.clone(), in |_cx| {});
drop(_parent);
let span = helper.assert_span_exists("test.clo_kind_ctx_exp");
assert_eq!(span.span_kind, SpanKind::Client);
assert_eq!(span.parent_span_id, parent_span_id);
}
#[test]
#[serial]
fn closure_kind_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.clo_kind_links_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
crate::span!("test.clo_kind_links", kind = SpanKind::Producer, links = [link], in |_cx| {});
let span = helper.assert_span_exists("test.clo_kind_links");
assert_eq!(span.span_kind, SpanKind::Producer);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_kind_context_links_attrs() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.clo_full_combo_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
crate::span!("test.clo_full_combo", kind = SpanKind::Consumer, context = None, links = [link], "mode" = "batch", in |_cx| {});
let span = helper.assert_span_exists("test.clo_full_combo");
assert_eq!(span.span_kind, SpanKind::Consumer);
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
helper.assert_span_has_attribute(&span, "mode", "batch");
}
#[test]
#[serial]
fn links_without_context() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.links_no_ctx_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let parent_cx = crate::span!(^ "test.links_no_ctx_parent", context = None);
let parent_span_id = parent_cx.span().span_context().span_id();
let _parent = parent_cx.attach();
{
crate::span!("test.links_no_ctx", links = [link]);
}
drop(_parent);
let span = helper.assert_span_exists("test.links_no_ctx");
assert_eq!(span.parent_span_id, parent_span_id);
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn detached_links_no_context() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.det_links_no_ctx_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
let cx = crate::span!(^ "test.det_links_no_ctx", links = [link]);
let _attached = cx.attach();
drop(_attached);
let span = helper.assert_span_exists("test.det_links_no_ctx");
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn closure_links_no_context() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.clo_links_no_ctx_tgt", context = None);
let linked_trace_id = linked_cx.span().span_context().trace_id();
let linked_span_id = linked_cx.span().span_context().span_id();
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
crate::span!("test.clo_links_no_ctx", links = [link], in |_cx| {});
let span = helper.assert_span_exists("test.clo_links_no_ctx");
assert_eq!(span.links.len(), 1);
helper.assert_link_matches(&span, 0, linked_trace_id, linked_span_id);
}
#[test]
#[serial]
fn explicit_tracer_kind_context_links() {
let helper = TestHelper::new();
let linked_cx = crate::span!(^ "test.exp_tracer_full_tgt", context = None);
let link = crate::link!(linked_cx.clone());
let _linked = linked_cx.attach();
drop(_linked);
{
crate::span!(@TRACER, "test.exp_tracer_kind_ctx_links", kind = SpanKind::Server, context = None, links = [link]);
}
let span = helper.assert_span_exists("test.exp_tracer_kind_ctx_links");
assert_eq!(span.span_kind, SpanKind::Server);
helper.assert_is_root_span(&span);
assert_eq!(span.links.len(), 1);
}