Nothing
skip_on_cran()
skip_if_not_installed("otelsdk")
with_otel_promise_domain({
describe("OpenTelemetry integration", {
describe("otel::start_span()", {
it("creates spans appropriately", {
records <- otelsdk::with_otel_record({
span <- otel::start_span("test_span0", tracer = NULL)
expect_true(inherits(span, "otel_span"))
otel::end_span(span)
})
expect_true(!is.null(records$traces[["test_span0"]]))
skip_if(!Sys.getenv("OTEL_TRACES_EXPORTER") %in% c("", "none"))
expect_s3_class(
otel::start_span("test_span"),
"otel_span_noop"
)
})
})
describe("Deprecated functions", {
it("with_ospan_async() is deprecated", {
expect_snapshot({
with_ospan_async(
"test_span_deprecated",
{
42
},
tracer = promises_otel_tracer()
)
})
})
it("with_ospan_promise_domain() is deprecated", {
expect_snapshot({
with_ospan_promise_domain({
42
})
})
})
it("local_ospan_promise_domain() is deprecated", {
expect_snapshot({
local({
local_ospan_promise_domain()
42
})
})
})
})
describe("with_otel_span()", {
it("executes synchronous expressions without otel", {
# When recording is off, should still execute expression
result <- with_otel_span(
"test_span1",
{
42
},
tracer = promises_otel_tracer()
)
expect_equal(result, 42)
})
it("executes synchronous expressions", {
records <- otelsdk::with_otel_record({
result <- with_otel_span(
"test_span1",
{
42
},
tracer = promises_otel_tracer()
)
expect_equal(result, 42)
})
expect_true(!is.null(records$traces[["test_span1"]]))
})
it("executes asynchronous expressions without otel", {
# When recording is off, should still execute expression
result <- with_otel_span(
"test_span1",
{
promise_resolve(42)
},
tracer = promises_otel_tracer()
)
expect_true(is.promising(result))
expect_equal(extract(result), 42)
})
it("handles promise results", {
records <- otelsdk::with_otel_record({
result <- with_otel_span(
"test_span2",
{
promise_resolve(42)
},
tracer = promises_otel_tracer()
)
expect_true(is.promising(result))
expect_equal(extract(result), 42)
})
expect_true(!is.null(records$traces[["test_span2"]]))
})
})
describe("promise domain integration", {
it("integrates with existing promise domains", {
# Test domain composition works even when otel is not available
counting_domain <- create_counting_domain()
p <- with_promise_domain(counting_domain, {
with_otel_span("test_operation", tracer = promises_otel_tracer(), {
promise_resolve(42) |>
then(function(x) x * 2)
})
})
result <- extract(p)
expect_equal(result, 84)
# Verify the counting domain was active
expect_gte(counting_domain$counts$onFulfilledBound, 1L)
})
})
describe("error handling", {
it("handles promise rejections gracefully", {
# Should handle rejected promises without errors
records <- otelsdk::with_otel_record({
p <- with_otel_span("test_span3", tracer = promises_otel_tracer(), {
promise_reject("test error") |>
catch(function(reason) {
with_otel_span("catch_span3", tracer = promises_otel_tracer(), {
42
})
expect_true(inherits(reason, "error"))
expect_match(as.character(reason), "test error")
"caught"
}) |>
then(function(result) {
expect_equal(result, "caught")
with_otel_span("then_span3", tracer = promises_otel_tracer(), {
42
})
})
})
wait_for_it(p)
p
})
expect_true(is.promising(p))
# Should be able to catch the error
test_id <- records$traces[["test_span3"]]$span_id
expect_equal(records$traces[["catch_span3"]]$parent, test_id)
expect_equal(records$traces[["then_span3"]]$parent, test_id)
})
it("propagates regular errors", {
# Regular errors should still propagate
expect_error(
with_otel_span("test_span4", tracer = promises_otel_tracer(), {
stop("regular error")
}),
"regular error"
)
})
})
it("maintains span context across parallel promise chains", {
# Track execution order and span context
execution_order <- character(0)
# Compare execution order after making `recording`
# Compare span parents using `recording`
recording <- otelsdk::with_otel_record({
# Create two parallel promise chains, each with their own span
chain1 <- with_otel_span("chain_1", tracer = promises_otel_tracer(), {
promise_resolve("init1") |>
then(function(x) {
spn <- otel::start_span("chain1_step1")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain1_step1")
paste0(x, "_step11")
}) |>
then(function(x) {
spn <- otel::start_span("chain1_step2")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain1_step2")
paste0(x, "_step12")
}) |>
then(function(x) {
spn <- otel::start_span("chain1_step3")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain1_step3")
paste0(x, "_step13")
}) |>
then(function(x) {
spn <- otel::start_span("chain1_step4")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain1_step4")
paste0(x, "_final1")
})
})
chain2 <- with_otel_span("chain_2", tracer = promises_otel_tracer(), {
promise_resolve("init2") |>
then(function(x) {
spn <- otel::start_span("chain2_step1")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain2_step1")
paste0(x, "_step12")
}) |>
then(function(x) {
spn <- otel::start_span("chain2_step2")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain2_step2")
paste0(x, "_step22")
}) |>
then(function(x) {
spn <- otel::start_span("chain2_step3")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain2_step3")
paste0(x, "_step23")
}) |>
then(function(x) {
spn <- otel::start_span("chain2_step4")
on.exit(otel::end_span(spn))
execution_order <<- c(execution_order, "chain2_step4")
paste0(x, "_final2")
})
})
# Wait for both chains to complete
result1 <- extract(chain1)
result2 <- extract(chain2)
# Verify final recording
expect_equal(result1, "init1_step11_step12_step13_final1")
expect_equal(result2, "init2_step12_step22_step23_final2")
# Check that execution alternates between chains
expected_alternating_pattern <- c(
"chain1_step1",
"chain2_step1",
"chain1_step2",
"chain2_step2",
"chain1_step3",
"chain2_step3",
"chain1_step4",
"chain2_step4"
)
expect_equal(
execution_order,
expected_alternating_pattern,
info = "Execution should alternate between chains"
)
})
# * Verify spans at each step have the same parent, even though the
# previous calculation may have been a different promise domain.
# * All steps in chain1 should have same parent span, all in chain2 should
# have same parent span.
chain1_id <- recording$traces[["chain_1"]]$span_id
for (name in c(
"chain1_step1",
"chain1_step2",
"chain1_step3",
"chain1_step4"
)) {
expect_equal(recording$traces[[name]]$parent, chain1_id)
}
chain2_id <- recording$traces[["chain_2"]]$span_id
for (name in c(
"chain2_step1",
"chain2_step2",
"chain2_step3",
"chain2_step4"
)) {
expect_equal(recording$traces[[name]]$parent, chain2_id)
}
})
})
})
describe("local_otel_promise_domain()", {
it("sets up otel span promise domain for local scope", {
# Test that the domain is active within the local scope
original_domain <- current_promise_domain()
local({
local_otel_promise_domain()
current_domain <- current_promise_domain()
# Should have an otel span domain set
expect_false(identical(current_domain, original_domain))
expect_true(has_otel_promise_domain(current_domain))
})
# Should be restored after exiting the local scope
expect_identical(current_promise_domain(), original_domain)
})
it("restores previous domain when scope exits", {
counting_domain <- create_counting_domain()
expect_equal(current_promise_domain(), NULL)
with_promise_domain(counting_domain, {
domain_before <- current_promise_domain()
local({
local_otel_promise_domain()
current_domain <- current_promise_domain()
# Should be a composed domain with otel span support
expect_false(identical(current_domain, domain_before))
expect_true(has_otel_promise_domain(current_domain))
})
# Should restore the counting domain
expect_identical(current_promise_domain(), domain_before)
})
expect_equal(current_promise_domain(), NULL)
})
# it("works with custom environment", {
# test_env <- new.env()
# original_domain <- current_promise_domain()
# # Set up domain in custom environment
# local_otel_promise_domain(envir = test_env)
# # Domain should still be active since test_env hasn't been cleaned
# current_domain <- current_promise_domain()
# expect_false(identical(current_domain, original_domain))
# expect_true(has_otel_promise_domain(current_domain))
# # Clean up the environment to trigger restoration
# rm(list = ls(test_env), envir = test_env)
# # Force garbage collection to ensure cleanup handlers run
# gc()
# # Domain should be restored (though this timing is implementation dependent)
# # So we mainly test that the function doesn't error with custom envir
# expect_true(TRUE)
# })
it("integrates with promise execution", {
records <- otelsdk::with_otel_record({
result <- local({
local_otel_promise_domain()
otel::start_local_active_span("outer_test_span")
# Create a promise within the otel span domain
promise_resolve(21) |>
then(function(x) {
# This should be executed within the otel span domain
span <- otel::start_span("inner_test_span")
on.exit(otel::end_span(span))
x * 2
})
})
wait_for_it(result)
result
})
expect_true(is.promising(result))
expect_equal(extract(result), 42)
expect_true(!is.null(records$traces[["outer_test_span"]]))
expect_true(!is.null(records$traces[["inner_test_span"]]))
expect_equal(
records$traces[["inner_test_span"]]$parent,
records$traces[["outer_test_span"]]$span_id
)
})
it("can be nested without issues", {
# Track how many times promise domain setup occurs
domain_creation_count <- 0
original_copd <- create_otel_promise_domain
# Mock the domain creation to count calls
with_mocked_bindings(
create_otel_promise_domain = function() {
domain_creation_count <<- domain_creation_count + 1
original_copd()
},
{
original_domain <- current_promise_domain()
local({
local_otel_promise_domain()
expect_equal(domain_creation_count, 1)
domain1 <- current_promise_domain()
local({
# This should not create another domain due to idempotency
local_otel_promise_domain()
expect_equal(domain_creation_count, 1) # Still 1, not 2
domain2 <- current_promise_domain()
# Both should have otel span domains and should be identical
expect_true(has_otel_promise_domain(domain1))
expect_true(has_otel_promise_domain(domain2))
expect_identical(domain1, domain2) # Should be the same due to idempotency
})
# Should still be domain1 after nested scope exits
expect_identical(current_promise_domain(), domain1)
})
# Should restore to original after all scopes exit
# Note: This may not be immediate due to deferred cleanup
expect_equal(domain_creation_count, 1) # Only created once total
}
)
})
})
describe("has_otel_promise_domain()", {
it("returns FALSE for NULL domain", {
expect_false(has_otel_promise_domain(NULL))
})
it("returns FALSE for empty list", {
expect_false(has_otel_promise_domain(list()))
})
it("returns FALSE for regular promise domain", {
regular_domain <- new_promise_domain(
wrapOnFulfilled = function(onFulfilled) onFulfilled
)
expect_false(has_otel_promise_domain(regular_domain))
})
it("returns TRUE for otel span promise domain", {
otel_span_domain <- create_otel_promise_domain()
expect_true(has_otel_promise_domain(otel_span_domain))
})
it("returns TRUE for composed domain with otel span", {
counting_domain <- create_counting_domain()
otel_span_domain <- create_otel_promise_domain()
composed_domain <- compose_domains(counting_domain, otel_span_domain)
expect_true(has_otel_promise_domain(composed_domain))
})
it("returns FALSE for composed domain without otel span", {
counting_domain1 <- create_counting_domain()
counting_domain2 <- create_counting_domain()
composed_domain <- compose_domains(counting_domain1, counting_domain2)
expect_false(has_otel_promise_domain(composed_domain))
})
it("uses current_promise_domain() when no argument provided", {
# Initially no domain
expect_false(has_otel_promise_domain())
# With otel span domain active
with_otel_promise_domain({
expect_true(has_otel_promise_domain())
})
# Back to no domain
expect_false(has_otel_promise_domain())
})
it("works with various data types", {
# # Test edge cases
# expect_false(has_otel_promise_domain(character(0)))
# expect_false(has_otel_promise_domain(numeric(0)))
# expect_false(has_otel_promise_domain("not a domain"))
# expect_false(has_otel_promise_domain(42))
# Test list with wrong flag
fake_domain <- list(.some_other_flag = TRUE)
expect_false(has_otel_promise_domain(fake_domain))
# Test list with correct flag
correct_domain <- list(.otel_promise_domain = TRUE)
expect_true(has_otel_promise_domain(correct_domain))
# Test list with correct flag but FALSE value
false_domain <- list(.otel_promise_domain = FALSE)
expect_false(has_otel_promise_domain(false_domain))
})
})
describe("with_otel_promise_domain() idempotency", {
it("is idempotent when called multiple times", {
# Track how many times promise domain setup occurs
domain_creation_count <- 0
reset_count <- function() {
domain_creation_count <<- 0
}
original_copd <- create_otel_promise_domain
expect_false(has_otel_promise_domain())
# Mock the domain creation to count calls
with_mocked_bindings(
create_otel_promise_domain = function() {
domain_creation_count <<- domain_creation_count + 1
original_copd()
},
{
# First call should create the domain
result1 <- with_otel_promise_domain({
42
})
expect_equal(result1, 42)
expect_equal(domain_creation_count, 1)
reset_count()
result2 <-
with_otel_promise_domain({
expect_equal(domain_creation_count, 1)
# Nested calls should not create additional domains
with_otel_promise_domain({
with_otel_promise_domain({
expect_equal(domain_creation_count, 1)
84
})
})
})
expect_equal(result2, 84)
# Should still be 1, even with two prom domain calls
expect_equal(domain_creation_count, 1)
}
)
})
it("allows nested calls without duplicate domain setup", {
# Test that nested with_otel_promise_domain calls work correctly
# and don't interfere with each other
records <- otelsdk::with_otel_record({
result <- with_otel_promise_domain({
with_otel_span("outer_span", tracer = promises_otel_tracer(), {
# This nested call should be idempotent
with_otel_promise_domain({
with_otel_span("inner_span", tracer = promises_otel_tracer(), {
promise_resolve(42) |>
then(function(x) {
# Another nested call
with_otel_promise_domain({
x * 2
})
})
})
})
})
})
wait_for_it(result)
result
})
expect_true(is.promising(result))
expect_equal(extract(result), 84)
# Verify spans were created correctly
expect_true(!is.null(records$traces[["outer_span"]]))
expect_true(!is.null(records$traces[["inner_span"]]))
# Verify parent-child relationship
outer_id <- records$traces[["outer_span"]]$span_id
expect_equal(records$traces[["inner_span"]]$parent, outer_id)
})
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.