bloodhound-1.0.0.0: tests/Test/IndexDocumentParamsSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.IndexDocumentParamsSpec (spec) where
import Data.Aeson (decode, encode)
import Data.List (sort)
import Data.Text (Text)
import Database.Bloodhound.Common.Requests (indexQueryString)
import Database.Bloodhound.Common.Types
import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)
-- | Stable ordering for equality. 'indexQueryString' preserves the
-- order in which params are emitted (legacy version-control first,
-- then routing, then the newer ones), but tests should not couple to
-- that internal ordering.
normalize :: [(Text, Maybe Text)] -> [(Text, Maybe Text)]
normalize = sort
spec :: Spec
spec =
describe "IndexDocumentSettings URI param rendering" $ do
it "defaultIndexDocumentSettings emits no params" $ do
indexQueryString defaultIndexDocumentSettings (DocId "1")
`shouldBe` []
it "JoinRelation ParentDocument emits routing = docId" $ do
let cfg =
defaultIndexDocumentSettings
{ idsJoinRelation =
Just (ParentDocument (FieldName "join") (RelationName "message"))
}
indexQueryString cfg (DocId "42")
`shouldBe` [("routing", Just "42")]
it "JoinRelation ChildDocument emits routing = parent docId" $ do
let cfg =
defaultIndexDocumentSettings
{ idsJoinRelation =
Just
( ChildDocument
(FieldName "join")
(RelationName "reply")
(DocId "parent-1")
)
}
indexQueryString cfg (DocId "child-2")
`shouldBe` [("routing", Just "parent-1")]
it "ExternalGTE version control emits version + version_type" $ do
let cfg =
defaultIndexDocumentSettings
{ idsVersionControl = ExternalGTE (ExternalDocVersion minBound)
}
normalize (indexQueryString cfg (DocId "1"))
`shouldBe` [ ("version", Just "1"),
("version_type", Just "external_gte")
]
describe "op_type" $ do
it "OpCreate renders as op_type=create" $ do
let cfg = defaultIndexDocumentSettings {idsOpType = Just OpCreate}
indexQueryString cfg (DocId "1")
`shouldBe` [("op_type", Just "create")]
it "OpIndex renders as op_type=index" $ do
let cfg = defaultIndexDocumentSettings {idsOpType = Just OpIndex}
indexQueryString cfg (DocId "1")
`shouldBe` [("op_type", Just "index")]
-- \| Regression coverage for the underlying 'OpType' type and the
-- @idsOpType@ lens (the URI-rendering tests above exercise the
-- downstream output only; these pin the type-level building blocks
-- so a refactor of 'OpType' or its instances cannot silently break
-- @op_type@ support). Bead bloodhound-04f.7.14.
describe "OpType type and lens (regression)" $ do
it "renderOpType yields the bare URI value" $ do
renderOpType OpCreate `shouldBe` "create"
renderOpType OpIndex `shouldBe` "index"
it "ToJSON renders as the quoted string ES expects in bodies" $ do
encode OpCreate `shouldBe` "\"create\""
encode OpIndex `shouldBe` "\"index\""
it "FromJSON round-trips both constructors" $ do
decode "\"create\"" `shouldBe` Just OpCreate
decode "\"index\"" `shouldBe` Just OpIndex
it "FromJSON rejects unknown values" $ do
(decode "\"upsert\"" :: Maybe OpType) `shouldBe` Nothing
it "idsOpType field round-trips set/get" $ do
let s = defaultIndexDocumentSettings {idsOpType = Just OpCreate}
idsOpType s `shouldBe` Just OpCreate
it "defaultIndexDocumentSettings has no op_type (Nothing)" $
idsOpType defaultIndexDocumentSettings `shouldBe` Nothing
it "pipeline renders the supplied pipeline id verbatim" $ do
let cfg = defaultIndexDocumentSettings {idsPipeline = Just "my-pipeline"}
indexQueryString cfg (DocId "1")
`shouldBe` [("pipeline", Just "my-pipeline")]
describe "refresh" $ do
it "RefreshTrue renders as refresh=true" $ do
let cfg = defaultIndexDocumentSettings {idsRefresh = Just RefreshTrue}
indexQueryString cfg (DocId "1")
`shouldBe` [("refresh", Just "true")]
it "RefreshFalse renders as refresh=false" $ do
let cfg = defaultIndexDocumentSettings {idsRefresh = Just RefreshFalse}
indexQueryString cfg (DocId "1")
`shouldBe` [("refresh", Just "false")]
it "RefreshWaitFor renders as refresh=wait_for" $ do
let cfg = defaultIndexDocumentSettings {idsRefresh = Just RefreshWaitFor}
indexQueryString cfg (DocId "1")
`shouldBe` [("refresh", Just "wait_for")]
describe "wait_for_active_shards" $ do
it "AllActiveShards renders as wait_for_active_shards=all" $ do
let cfg =
defaultIndexDocumentSettings
{ idsWaitForActiveShards = Just AllActiveShards
}
indexQueryString cfg (DocId "1")
`shouldBe` [("wait_for_active_shards", Just "all")]
it "ActiveShards n renders as wait_for_active_shards=<n>" $ do
let cfg =
defaultIndexDocumentSettings
{ idsWaitForActiveShards = Just (ActiveShards 3)
}
indexQueryString cfg (DocId "1")
`shouldBe` [("wait_for_active_shards", Just "3")]
describe "if_seq_no / if_primary_term" $ do
it "emits both when both are set" $ do
let cfg =
defaultIndexDocumentSettings
{ idsIfSeqNo = Just 7,
idsIfPrimaryTerm = Just 2
}
normalize (indexQueryString cfg (DocId "1"))
`shouldBe` [ ("if_primary_term", Just "2"),
("if_seq_no", Just "7")
]
it "emits if_seq_no alone when only if_seq_no is set (pass-through to ES 400)" $ do
let cfg = defaultIndexDocumentSettings {idsIfSeqNo = Just 7}
indexQueryString cfg (DocId "1")
`shouldBe` [("if_seq_no", Just "7")]
it "emits if_primary_term alone when only if_primary_term is set (pass-through to ES 400)" $ do
let cfg = defaultIndexDocumentSettings {idsIfPrimaryTerm = Just 2}
indexQueryString cfg (DocId "1")
`shouldBe` [("if_primary_term", Just "2")]
describe "require_alias" $ do
it "Just True renders as require_alias=true" $ do
let cfg = defaultIndexDocumentSettings {idsRequireAlias = Just True}
indexQueryString cfg (DocId "1")
`shouldBe` [("require_alias", Just "true")]
it "Just False renders as require_alias=false" $ do
let cfg = defaultIndexDocumentSettings {idsRequireAlias = Just False}
indexQueryString cfg (DocId "1")
`shouldBe` [("require_alias", Just "false")]
describe "timeout" $ do
it "(TimeUnitSeconds, 30) renders as timeout=30s" $ do
let cfg = defaultIndexDocumentSettings {idsTimeout = Just (TimeUnitSeconds, 30)}
indexQueryString cfg (DocId "1")
`shouldBe` [("timeout", Just "30s")]
it "(TimeUnitMilliseconds, 200) renders as timeout=200ms" $ do
let cfg = defaultIndexDocumentSettings {idsTimeout = Just (TimeUnitMilliseconds, 200)}
indexQueryString cfg (DocId "1")
`shouldBe` [("timeout", Just "200ms")]
it "(TimeUnitMinutes, 1) renders as timeout=1m (matches server default shape)" $ do
let cfg = defaultIndexDocumentSettings {idsTimeout = Just (TimeUnitMinutes, 1)}
indexQueryString cfg (DocId "1")
`shouldBe` [("timeout", Just "1m")]
it "defaultIndexDocumentSettings has no timeout (Nothing)" $
idsTimeout defaultIndexDocumentSettings `shouldBe` Nothing
it "idsTimeout field round-trips set/get" $ do
let s = defaultIndexDocumentSettings {idsTimeout = Just (TimeUnitSeconds, 5)}
idsTimeout s `shouldBe` Just (TimeUnitSeconds, 5)
it "renders every field together when all are populated" $ do
let cfg =
defaultIndexDocumentSettings
{ idsOpType = Just OpCreate,
idsPipeline = Just "p",
idsRefresh = Just RefreshWaitFor,
idsWaitForActiveShards = Just AllActiveShards,
idsIfSeqNo = Just 10,
idsIfPrimaryTerm = Just 1,
idsRequireAlias = Just True,
idsTimeout = Just (TimeUnitSeconds, 5)
}
let rendered = indexQueryString cfg (DocId "1")
length rendered `shouldBe` 8
-- every value is non-Nothing
rendered `shouldSatisfy` all (isJust . snd)
normalize rendered
`shouldBe` sort
[ ("if_primary_term", Just "1"),
("if_seq_no", Just "10"),
("op_type", Just "create"),
("pipeline", Just "p"),
("refresh", Just "wait_for"),
("require_alias", Just "true"),
("timeout", Just "5s"),
("wait_for_active_shards", Just "all")
]
it "preserves backwards compatibility: legacy version + join emit only the old params" $ do
let cfg =
defaultIndexDocumentSettings
{ idsVersionControl = ExternalGTE (ExternalDocVersion minBound),
idsJoinRelation =
Just (ParentDocument (FieldName "j") (RelationName "message"))
}
normalize (indexQueryString cfg (DocId "abc"))
`shouldBe` [ ("routing", Just "abc"),
("version", Just "1"),
("version_type", Just "external_gte")
]
where
isJust (Just _) = True
isJust Nothing = False