packages feed

bloodhound-1.0.0.0: tests/Test/ClusterHealthSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module Test.ClusterHealthSpec where

import Data.List qualified as L
import Data.Text qualified as T
import Database.Bloodhound.Common.Client qualified as Client
import TestsUtils.Common
import TestsUtils.Import

spec :: Spec
spec = do
  describe "cluster health API" $ do
    it "returns cluster health information" $
      withTestEnv $ do
        health <- Client.getClusterHealth
        liftIO $ clusterHealthClusterName health `shouldSatisfy` (not . T.null)
        liftIO $ clusterHealthNumberOfNodes health `shouldSatisfy` (> 0)
        liftIO $ clusterHealthNumberOfDataNodes health `shouldSatisfy` (> 0)

    it "returns valid health status" $
      withTestEnv $ do
        health <- Client.getClusterHealth
        liftIO $ clusterHealthStatus health `shouldSatisfy` (`elem` [ClusterHealthGreen, ClusterHealthYellow, ClusterHealthRed])

    it "returns shard information" $
      withTestEnv $ do
        health <- Client.getClusterHealth
        liftIO $ clusterHealthActivePrimaryShards health `shouldSatisfy` (>= 0)
        liftIO $ clusterHealthActiveShards health `shouldSatisfy` (>= 0)

    -- ------------------------------------------------------------------ --
    -- Per-index + options (bloodhound-04f.4.1.7)                          --
    -- ------------------------------------------------------------------ --
    it "getClusterHealthForIndex returns health for a specific index" $
      withTestEnv $ do
        let idx = [qqIndexName|bloodhound-tests-cluster-health-per-index|]
        _ <- tryPerformBHRequest $ deleteIndex idx
        _ <-
          performBHRequest $
            createIndex
              (IndexSettings (ShardCount 1) (ReplicaCount 0) defaultIndexMappingsLimits)
              idx
        health <- Client.getClusterHealthForIndex idx
        liftIO $ clusterHealthStatus health `shouldSatisfy` (`elem` [ClusterHealthGreen, ClusterHealthYellow, ClusterHealthRed])
        liftIO $ clusterHealthActivePrimaryShards health `shouldSatisfy` (>= 0)
        _ <- tryPerformBHRequest $ deleteIndex idx
        pure ()

    it "getClusterHealthWith defaultClusterHealthOptions returns a usable response" $
      withTestEnv $ do
        health <- Client.getClusterHealthWith defaultClusterHealthOptions
        liftIO $ clusterHealthClusterName health `shouldSatisfy` (not . T.null)

    it "getClusterHealthWith accepts level=indices (passthrough)" $
      withTestEnv $ do
        let opts =
              defaultClusterHealthOptions
                { choLevel = Just ClusterHealthLevelIndices
                }
        health <- Client.getClusterHealthWith opts
        -- The base fields are always present regardless of level; the
        -- indices/shards breakdown is accepted but not modelled yet.
        liftIO $ clusterHealthNumberOfNodes health `shouldSatisfy` (> 0)

    it "waitForYellowIndex still returns a health record (regression)" $
      withTestEnv $ do
        let idx = [qqIndexName|bloodhound-tests-cluster-health-wait-yellow|]
        _ <- tryPerformBHRequest $ deleteIndex idx
        _ <-
          performBHRequest $
            createIndex
              (IndexSettings (ShardCount 1) (ReplicaCount 0) defaultIndexMappingsLimits)
              idx
        status <- performBHRequest $ waitForYellowIndex idx
        liftIO $ healthStatusStatus status `shouldSatisfy` (`elem` ["green", "yellow"])
        _ <- tryPerformBHRequest $ deleteIndex idx
        pure ()

  -- ------------------------------------------------------------------ --
  -- clusterHealthOptionsParams: pure URI rendering (no ES required)     --
  -- ------------------------------------------------------------------ --
  describe "clusterHealthOptionsParams URI rendering" $ do
    let normalize :: [(T.Text, Maybe T.Text)] -> [(T.Text, Maybe T.Text)]
        normalize = L.sort

    it "defaultClusterHealthOptions emits no params" $
      clusterHealthOptionsParams defaultClusterHealthOptions
        `shouldBe` []

    it "renders level via renderClusterHealthLevel" $ do
      let opts =
            defaultClusterHealthOptions
              { choLevel = Just ClusterHealthLevelShards
              }
      clusterHealthOptionsParams opts
        `shouldBe` [("level", Just "shards")]

    it "renders wait_for_status as the status string" $ do
      let opts =
            defaultClusterHealthOptions
              { choWaitForStatus = Just ClusterHealthGreen
              }
      clusterHealthOptionsParams opts
        `shouldBe` [("wait_for_status", Just "green")]

    it "renders wait_for_active_shards via ActiveShardCount" $ do
      let opts =
            defaultClusterHealthOptions
              { choWaitForActiveShards = Just AllActiveShards
              }
      clusterHealthOptionsParams opts
        `shouldBe` [("wait_for_active_shards", Just "all")]

    it "renders wait_for_nodes verbatim" $ do
      let opts =
            defaultClusterHealthOptions
              { choWaitForNodes = Just "ge(2)"
              }
      clusterHealthOptionsParams opts
        `shouldBe` [("wait_for_nodes", Just "ge(2)")]

    it "renders booleans as lowercase true/false" $ do
      let opts =
            defaultClusterHealthOptions
              { choWaitForNoRelocatingShards = Just True,
                choWaitForNoInitializingShards = Just False,
                choLocal = Just True
              }
      normalize (clusterHealthOptionsParams opts)
        `shouldBe` [ ("local", Just "true"),
                     ("wait_for_no_initializing_shards", Just "false"),
                     ("wait_for_no_relocating_shards", Just "true")
                   ]

    it "renders master_timeout and timeout as <n><suffix>" $ do
      let opts =
            defaultClusterHealthOptions
              { choMasterTimeout = Just (TimeUnitSeconds, 30),
                choTimeout = Just (TimeUnitMilliseconds, 500)
              }
      normalize (clusterHealthOptionsParams opts)
        `shouldBe` [ ("master_timeout", Just "30s"),
                     ("timeout", Just "500ms")
                   ]

    it "emits every param together when all are set" $ do
      let opts =
            defaultClusterHealthOptions
              { choLevel = Just ClusterHealthLevelIndices,
                choLocal = Just True,
                choMasterTimeout = Just (TimeUnitMinutes, 1),
                choWaitForStatus = Just ClusterHealthYellow,
                choWaitForActiveShards = Just (ActiveShards 2),
                choWaitForNodes = Just "le(3)",
                choWaitForNoRelocatingShards = Just True,
                choWaitForNoInitializingShards = Just True,
                choTimeout = Just (TimeUnitSeconds, 10)
              }
      normalize (clusterHealthOptionsParams opts)
        `shouldBe` [ ("level", Just "indices"),
                     ("local", Just "true"),
                     ("master_timeout", Just "1m"),
                     ("timeout", Just "10s"),
                     ("wait_for_active_shards", Just "2"),
                     ("wait_for_no_initializing_shards", Just "true"),
                     ("wait_for_no_relocating_shards", Just "true"),
                     ("wait_for_nodes", Just "le(3)"),
                     ("wait_for_status", Just "yellow")
                   ]