bloodhound-1.0.0.0: tests/Test/VotingConfigExclusionsSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.VotingConfigExclusionsSpec where
import Data.ByteString.Lazy.Char8 qualified as BL
import Data.List qualified as L
import Data.Text qualified as T
import Database.Bloodhound.Client.Cluster (BackendType (..))
import Database.Bloodhound.Common.Requests qualified as Common
import TestsUtils.Common
import TestsUtils.Import
spec :: Spec
spec = do
-- ---------------------------------------------------------------- --
-- votingConfigExclusionOptionsParams: pure URI rendering (no ES). --
-- ---------------------------------------------------------------- --
describe "votingConfigExclusionOptionsParams URI rendering" $ do
let normalize :: [(T.Text, Maybe T.Text)] -> [(T.Text, Maybe T.Text)]
normalize = L.sort
it "defaultVotingConfigExclusionOptions emits no params" $
votingConfigExclusionOptionsParams defaultVotingConfigExclusionOptions
`shouldBe` []
it "renders node_ids as a comma-separated list" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds =
[FullNodeId "node-a", FullNodeId "node-b"]
}
votingConfigExclusionOptionsParams opts
`shouldBe` [("node_ids", Just "node-a,node-b")]
it "renders node_names as a comma-separated list" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeNames =
[NodeName "node-1", NodeName "node-2", NodeName "node-3"]
}
votingConfigExclusionOptionsParams opts
`shouldBe` [("node_names", Just "node-1,node-2,node-3")]
it "renders both node_ids and node_names together" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "a1"],
votingConfigExclusionOptionsNodeNames = [NodeName "n1"]
}
normalize (votingConfigExclusionOptionsParams opts)
`shouldBe` [ ("node_ids", Just "a1"),
("node_names", Just "n1")
]
it "omits an empty list rather than rendering an empty string" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [],
votingConfigExclusionOptionsNodeNames = [NodeName "only-name"]
}
votingConfigExclusionOptionsParams opts
`shouldBe` [("node_names", Just "only-name")]
-- ---------------------------------------------------------------- --
-- updateVotingConfigExclusions endpoint shape: pure checks against --
-- the BHRequest value; no live backend needed. --
-- ---------------------------------------------------------------- --
describe "updateVotingConfigExclusions endpoint shape" $ do
it "POSTs /_cluster/voting_config_exclusions" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "x"]
}
req = Common.updateVotingConfigExclusions opts
getRawEndpoint (bhRequestEndpoint req)
`shouldBe` ["_cluster", "voting_config_exclusions"]
bhRequestMethod req `shouldBe` "POST"
it "forwards node_ids and node_names as query parameters" $ do
let opts =
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "a", FullNodeId "b"],
votingConfigExclusionOptionsNodeNames = [NodeName "n"]
}
req = Common.updateVotingConfigExclusions opts
getRawEndpointQueries (bhRequestEndpoint req)
`shouldBe` [("node_ids", Just "a,b"), ("node_names", Just "n")]
it "sends the empty body (ES requires no body on this endpoint)" $ do
let req =
Common.updateVotingConfigExclusions
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "x"]
}
-- emptyBody is "" — distinct from no body at all (which would
-- be 'Nothing'); POSTs to this endpoint carry an empty string
-- so the server sees a body-shaped request without content.
bhRequestBody req `shouldBe` Just (BL.pack "")
-- ---------------------------------------------------------------- --
-- clearVotingConfigExclusions endpoint shape: pure checks. --
-- ---------------------------------------------------------------- --
describe "clearVotingConfigExclusions endpoint shape" $ do
it "DELETEs /_cluster/voting_config_exclusions" $ do
let req = Common.clearVotingConfigExclusions
getRawEndpoint (bhRequestEndpoint req)
`shouldBe` ["_cluster", "voting_config_exclusions"]
bhRequestMethod req `shouldBe` "DELETE"
it "sends no query string" $ do
let req = Common.clearVotingConfigExclusions
getRawEndpointQueries (bhRequestEndpoint req) `shouldBe` []
it "sends no body" $ do
let req = Common.clearVotingConfigExclusions
bhRequestBody req `shouldBe` Nothing
-- ---------------------------------------------------------------- --
-- Distinctness guard: prevents a copy-paste regression that --
-- re-points updateVotingConfigExclusions at clearVotingConfig- --
-- Exclusions's DELETE (or vice versa). Same precedent as the --
-- clearKnnCache/warmupKnnIndex guard in Test.KnnClearCacheSpec. --
-- ---------------------------------------------------------------- --
describe "POST/DELETE distinctness" $
it "the two endpoints differ in HTTP method" $ do
let postReq =
Common.updateVotingConfigExclusions
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "x"]
}
deleteReq = Common.clearVotingConfigExclusions
bhRequestMethod postReq `shouldNotBe` bhRequestMethod deleteReq
-- ---------------------------------------------------------------- --
-- Live round-trip: gated on Elasticsearch backends only (OpenSearch --
-- does not expose voting_config_exclusions). Verifies the empty-body --
-- 200 -> Acknowledged True mapping that 'parseEsResponse' now handles --
-- via null-substitution (see the Haddock on --
-- 'updateVotingConfigExclusions' and 'Test.ParseEsResponseSpec'). --
-- --
-- The endpoint is self-cleaning: calling 'clearVotingConfigExclusions' --
-- in the test body removes whatever the prior 'updateVotingConfig- --
-- Exclusions' call added. We still wrap the round-trip in a --
-- 'bracket_' that issues a defensive clear on teardown so a thrown --
-- exception or unexpected 4xx from the POST can't leak test state. --
-- ---------------------------------------------------------------- --
describe "voting_config_exclusions live round-trip" $
backendSpecific [ElasticSearch7, ElasticSearch8, ElasticSearch9] $ do
let clearAll =
withTestEnv $
void $
tryPerformBHRequest Common.clearVotingConfigExclusions
before_ clearAll . after_ clearAll $
it "adds an exclusion for a fake node id, then sees the clear land as Acknowledged" $ do
withTestEnv $ do
Acknowledged added <-
performBHRequest $
Common.updateVotingConfigExclusions
defaultVotingConfigExclusionOptions
{ votingConfigExclusionOptionsNodeIds = [FullNodeId "fake-node-id"]
}
liftIO $ added `shouldBe` True
Acknowledged cleared <-
performBHRequest Common.clearVotingConfigExclusions
liftIO $ cleared `shouldBe` True