packages feed

bloodhound-1.0.0.0: tests/Test/StreamsSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.StreamsSpec (spec) where

import Data.Aeson
import Data.ByteString.Lazy.Char8 qualified as LBS
import Database.Bloodhound.ElasticSearch9.Requests qualified as RequestsES9
import Database.Bloodhound.ElasticSearch9.Types qualified as Types
import TestsUtils.Import

spec :: Spec
spec = describe "Streams API (/_streams/*)" $ do
  describe "StreamName JSON" $ do
    it "round-trips a StreamName" $ do
      let n = Types.StreamName "logs.otel"
      decode (encode n) `shouldBe` Just n

    it "unStreamName extracts the underlying Text" $
      Types.unStreamName (Types.StreamName "logs")
        `shouldBe` ("logs" :: Text)

  describe "StreamStatus JSON" $ do
    it "encodes as {enabled: bool}" $ do
      encode (Types.StreamStatus True)
        `shouldBe` "{\"enabled\":true}"
      encode (Types.StreamStatus False)
        `shouldBe` "{\"enabled\":false}"

    it "decodes {enabled: true}" $
      decode (LBS.pack "{\"enabled\":true}")
        `shouldBe` Just (Types.StreamStatus True)

  describe "StreamsStatusResponse JSON" $ do
    it "decodes a map of named streams (keys contain dots)" $ do
      let raw =
            LBS.pack
              "{\"logs\":{\"enabled\":true},\"logs.otel\":{\"enabled\":false},\"logs.ecs\":{\"enabled\":true}}"
      case decode raw :: Maybe Types.StreamsStatusResponse of
        Just resp -> do
          Types.streamStatusFor "logs" resp
            `shouldBe` Just (Types.StreamStatus True)
          Types.streamStatusFor "logs.otel" resp
            `shouldBe` Just (Types.StreamStatus False)
          Types.streamStatusFor "logs.ecs" resp
            `shouldBe` Just (Types.StreamStatus True)
          Types.streamStatusFor "missing" resp
            `shouldBe` (Nothing :: Maybe Types.StreamStatus)
        Nothing ->
          expectationFailure "failed to decode StreamsStatusResponse"

    it "decodes an empty object to an empty map" $
      case decode (LBS.pack "{}") :: Maybe Types.StreamsStatusResponse of
        Just resp ->
          Types.streamStatusFor "anything" resp
            `shouldBe` (Nothing :: Maybe Types.StreamStatus)
        Nothing ->
          expectationFailure "failed to decode empty StreamsStatusResponse"

    it "round-trips a populated response" $ do
      let raw =
            LBS.pack
              "{\"logs\":{\"enabled\":true},\"logs.ecs\":{\"enabled\":false}}"
      case decode raw :: Maybe Types.StreamsStatusResponse of
        Just resp -> decode (encode resp) `shouldBe` Just resp
        Nothing -> expectationFailure "failed to decode StreamsStatusResponse"

  describe "streamsActionOptionsParams URI rendering" $ do
    it "defaultStreamsActionOptions emits no params" $
      Types.streamsActionOptionsParams Types.defaultStreamsActionOptions
        `shouldBe` []

    it "renders master_timeout and timeout" $ do
      let opts =
            Types.defaultStreamsActionOptions
              { Types.saoMasterTimeout = Just "30s",
                Types.saoTimeout = Just "1m"
              }
      Types.streamsActionOptionsParams opts
        `shouldBe` [("master_timeout", Just "30s"), ("timeout", Just "1m")]

    it "renders only the set field" $ do
      let opts =
            Types.defaultStreamsActionOptions
              { Types.saoTimeout = Just "1m"
              }
      Types.streamsActionOptionsParams opts
        `shouldBe` [("timeout", Just "1m")]

  describe "getStreamsStatusOptionsParams URI rendering" $ do
    it "defaultGetStreamsStatusOptions emits no params" $
      Types.getStreamsStatusOptionsParams Types.defaultGetStreamsStatusOptions
        `shouldBe` []

    it "renders master_timeout" $ do
      let opts =
            Types.defaultGetStreamsStatusOptions
              { Types.gssoMasterTimeout = Just (TimeUnitSeconds, 30)
              }
      Types.getStreamsStatusOptionsParams opts
        `shouldBe` [("master_timeout", Just "30s")]

    it "renders a minute duration with the m suffix" $ do
      let opts =
            Types.defaultGetStreamsStatusOptions
              { Types.gssoMasterTimeout = Just (TimeUnitMinutes, 1)
              }
      Types.getStreamsStatusOptionsParams opts
        `shouldBe` [("master_timeout", Just "1m")]

  describe "endpoint shape" $ do
    it "GETs /_streams/status with no body" $ do
      let req = RequestsES9.getStreamsStatus
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "status"]
      bhRequestBody req `shouldSatisfy` isNothing

    it "getStreamsStatusWith appends master_timeout query param" $ do
      let opts =
            Types.defaultGetStreamsStatusOptions
              { Types.gssoMasterTimeout = Just (TimeUnitSeconds, 30)
              }
          req = RequestsES9.getStreamsStatusWith opts
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "status"]
      getRawEndpointQueries (bhRequestEndpoint req)
        `shouldBe` [("master_timeout", Just "30s")]

    it "POSTs /_streams/<name>/_enable with an empty body" $ do
      let req = RequestsES9.enableStream "logs"
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "logs", "_enable"]
      bhRequestBody req `shouldSatisfy` isJust

    it "enableStreamWith appends timeout query params" $ do
      let opts =
            Types.defaultStreamsActionOptions
              { Types.saoMasterTimeout = Just "10s"
              }
          req = RequestsES9.enableStreamWith "logs.otel" opts
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "logs.otel", "_enable"]
      getRawEndpointQueries (bhRequestEndpoint req)
        `shouldBe` [("master_timeout", Just "10s")]

    it "POSTs /_streams/<name>/_disable with an empty body" $ do
      let req = RequestsES9.disableStream "logs.ecs"
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "logs.ecs", "_disable"]
      bhRequestBody req `shouldSatisfy` isJust

    it "disableStreamWith appends timeout query params" $ do
      let opts =
            Types.defaultStreamsActionOptions
              { Types.saoTimeout = Just "5s"
              }
          req = RequestsES9.disableStreamWith "logs" opts
      getRawEndpoint (bhRequestEndpoint req)
        `shouldBe` ["_streams", "logs", "_disable"]
      getRawEndpointQueries (bhRequestEndpoint req)
        `shouldBe` [("timeout", Just "5s")]