packages feed

kafka-effectful-0.3.1.0: test/Kafka/Effectful/OpenTelemetry/ShibuyaCompatibilityTest.hs

module Kafka.Effectful.OpenTelemetry.ShibuyaCompatibilityTest (tests) where

import Data.HashMap.Lazy qualified as HashMap
import Data.Int (Int64)
import Data.Text (Text)
import Kafka.Consumer.Types
  ( ConsumerRecord (..),
    Offset (..),
    Timestamp (NoTimestamp),
  )
import Kafka.Effectful.OpenTelemetry.Semantic (consumerRecordAttributes)
import Kafka.Types
  ( PartitionId (..),
    TopicName (..),
    headersFromList,
  )
import OpenTelemetry.Attributes (Attribute, toAttribute, unkey)
import OpenTelemetry.SemanticConventions
  ( messaging_kafka_destination_partition,
    messaging_kafka_message_offset,
  )
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertEqual, testCase)

-- | Tests pinning @Kafka.Effectful.OpenTelemetry.Semantic@\'s legacy
-- 'consumerRecordAttributes' helper against the attribute set that
-- @shibuya-kafka-adapter@\'s
-- @Shibuya.Adapter.Kafka.Convert.kafkaSpanAttributes@ produces.
--
-- Both libraries depend on @hs-opentelemetry-semantic-conventions@,
-- so if upstream renames a key in a future release this test fails
-- together with shibuya for the legacy-mode compatibility pin. Stable
-- OpenTelemetry messaging keys are covered separately in
-- @SemanticTest@.
tests :: TestTree
tests =
  testGroup
    "ShibuyaCompatibility"
    [ testCase "consumerRecordAttributes agrees on messaging.system" $ do
        let actual = HashMap.lookup "messaging.system" sampleAttrs
            expected = HashMap.lookup "messaging.system" shibuyaAttrs
        assertEqual "messaging.system attribute differs" expected actual,
      testCase "consumerRecordAttributes agrees on messaging.kafka.destination.partition" $ do
        let key = unkey messaging_kafka_destination_partition
            actual = HashMap.lookup key sampleAttrs
            expected = HashMap.lookup key shibuyaAttrs
        assertEqual "partition attribute differs" expected actual,
      testCase "consumerRecordAttributes agrees on messaging.kafka.message.offset" $ do
        let key = unkey messaging_kafka_message_offset
            actual = HashMap.lookup key sampleAttrs
            expected = HashMap.lookup key shibuyaAttrs
        assertEqual "offset attribute differs" expected actual
    ]

-- Sample inputs — partition 7, offset 42 — chosen so the resulting
-- attribute values are non-zero and easy to spot in failures.

samplePid :: PartitionId
samplePid = PartitionId 7

sampleOffset :: Offset
sampleOffset = Offset 42

sampleAttrs :: HashMap.HashMap Text Attribute
sampleAttrs =
  consumerRecordAttributes
    ConsumerRecord
      { crTopic = TopicName "orders",
        crPartition = samplePid,
        crOffset = sampleOffset,
        crTimestamp = NoTimestamp,
        crHeaders = headersFromList [],
        crKey = Nothing,
        crValue = Just "value"
      }

-- | Local replica of @shibuya-kafka-adapter@\'s
-- @Shibuya.Adapter.Kafka.Convert.kafkaSpanAttributes@. Mirrors the
-- exact source-level expression at
-- @shibuya-kafka-adapter\/src\/Shibuya\/Adapter\/Kafka\/Convert.hs:78-89@:
--
-- > kafkaSpanAttributes :: PartitionId -> Offset -> HashMap Text Attribute
-- > kafkaSpanAttributes (PartitionId pid) (Offset off) =
-- >     HashMap.fromList
-- >         [ (\"messaging.system\", toAttribute (\"kafka\" :: Text))
-- >         , (unkey messaging_kafka_destination_partition, toAttribute (fromIntegral pid :: Int64))
-- >         , (unkey messaging_kafka_message_offset, toAttribute (off :: Int64))
-- >         ]
shibuyaAttrs :: HashMap.HashMap Text Attribute
shibuyaAttrs =
  let PartitionId pid = samplePid
      Offset off = sampleOffset
   in HashMap.fromList
        [ ("messaging.system", toAttribute ("kafka" :: Text)),
          ( unkey messaging_kafka_destination_partition,
            toAttribute (fromIntegral pid :: Int64)
          ),
          ( unkey messaging_kafka_message_offset,
            toAttribute (off :: Int64)
          )
        ]