packages feed

hs-opentelemetry-api-1.0.0.0: test/OpenTelemetry/Context/PropagationSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module OpenTelemetry.Context.PropagationSpec where

import Control.Concurrent.Async (wait)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import OpenTelemetry.Context (empty, insert, lookup, newKey)
import OpenTelemetry.Context.ThreadLocal (attachContext, detachContext, getContext)
import OpenTelemetry.Context.ThreadLocal.Propagation
import Test.Hspec
import Prelude hiding (lookup)


spec :: Spec
spec = describe "Context.ThreadLocal.Propagation" $ do
  -- Context API §Context: propagate implicit context across continuations
  -- https://opentelemetry.io/docs/specs/otel/context/
  describe "propagateContext" $ do
    it "captures caller context for deferred execution" $ do
      k <- newKey "prop-test"
      let ctx = insert k (42 :: Int) empty
      tok <- attachContext ctx
      wrapped <- propagateContext $ do
        ctx' <- getContext
        pure (lookup k ctx')
      detachContext tok
      result <- wrapped
      result `shouldBe` Just 42

  describe "tracedForkIO" $ do
    it "child thread inherits caller context" $ do
      k <- newKey "fork-test"
      let ctx = insert k (99 :: Int) empty
      tok <- attachContext ctx
      mv <- newEmptyMVar
      _ <- tracedForkIO $ do
        ctx' <- getContext
        putMVar mv (lookup k ctx')
      result <- takeMVar mv
      result `shouldBe` Just 99
      detachContext tok

  describe "tracedAsync" $ do
    it "async task inherits caller context" $ do
      k <- newKey "async-test"
      let ctx = insert k (7 :: Int) empty
      tok <- attachContext ctx
      a <- tracedAsync $ do
        ctx' <- getContext
        pure (lookup k ctx')
      result <- wait a
      result `shouldBe` Just 7
      detachContext tok

  describe "tracedConcurrently" $ do
    it "both branches inherit caller context" $ do
      k <- newKey "conc-test"
      let ctx = insert k (33 :: Int) empty
      tok <- attachContext ctx
      (r1, r2) <-
        tracedConcurrently
          (do ctx' <- getContext; pure (lookup k ctx'))
          (do ctx' <- getContext; pure (lookup k ctx'))
      r1 `shouldBe` Just 33
      r2 `shouldBe` Just 33
      detachContext tok

  describe "tracedMapConcurrently" $ do
    it "all workers inherit caller context" $ do
      k <- newKey "map-conc"
      let ctx = insert k (55 :: Int) empty
      tok <- attachContext ctx
      results <- tracedMapConcurrently (\_ -> do ctx' <- getContext; pure (lookup k ctx')) [1 :: Int, 2, 3]
      results `shouldBe` [Just 55, Just 55, Just 55]
      detachContext tok

  describe "tracedForConcurrently" $ do
    it "all workers inherit caller context" $ do
      k <- newKey "for-conc"
      let ctx = insert k (88 :: Int) empty
      tok <- attachContext ctx
      results <- tracedForConcurrently [1 :: Int, 2] $ \_ -> do
        ctx' <- getContext
        pure (lookup k ctx')
      results `shouldBe` [Just 88, Just 88]
      detachContext tok

  describe "tracedWithAsync" $ do
    it "async task inherits caller context" $ do
      k <- newKey "with-async"
      let ctx = insert k (66 :: Int) empty
      tok <- attachContext ctx
      result <- tracedWithAsync (do ctx' <- getContext; pure (lookup k ctx')) wait
      result `shouldBe` Just 66
      detachContext tok