bloodhound-1.0.0.0: tests/Test/SearchOptionsSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.SearchOptionsSpec (spec) where
import Data.List (sort)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Maybe (isJust)
import Data.Text (Text)
import Database.Bloodhound.Common.Requests (searchOptionsParams)
import Database.Bloodhound.Common.Types
import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)
-- | Stable ordering for equality: 'withQueries' preserves the order in
-- which params are emitted, but the tests compare the @Set@ of params to
-- avoid coupling to internal field ordering of 'SearchOptions'.
normalize :: [(Text, Maybe Text)] -> [(Text, Maybe Text)]
normalize = sort
spec :: Spec
spec =
describe "SearchOptions URI param rendering" $ do
it "defaultSearchOptions emits no params with the default SearchType" $ do
searchOptionsParams defaultSearchOptions SearchTypeQueryThenFetch
`shouldBe` []
it "defaultSearchOptions + DfsQueryThenFetch emits only search_type" $ do
searchOptionsParams defaultSearchOptions SearchTypeDfsQueryThenFetch
`shouldBe` [("search_type", Just "dfs_query_then_fetch")]
it "emits search_type even when other params are set" $ do
let opts =
defaultSearchOptions
{ soPreference = Just "_local"
}
normalize (searchOptionsParams opts SearchTypeDfsQueryThenFetch)
`shouldBe` [ ("preference", Just "_local"),
("search_type", Just "dfs_query_then_fetch")
]
it "renders every Bool field as true/false strings" $ do
let opts =
defaultSearchOptions
{ soAllowNoIndices = Just True,
soIgnoreUnavailable = Just False,
soRequestCache = Just True,
soTypedKeys = Just False,
soSeqNoPrimaryTerm = Just True,
soCcsMinimizeRoundtrips = Just False,
soAllowPartialSearchResults = Just True
}
normalize (searchOptionsParams opts SearchTypeQueryThenFetch)
`shouldBe` [ ("allow_no_indices", Just "true"),
("allow_partial_search_results", Just "true"),
("ccs_minimize_roundtrips", Just "false"),
("ignore_unavailable", Just "false"),
("request_cache", Just "true"),
("seq_no_primary_term", Just "true"),
("typed_keys", Just "false")
]
it "renders every Int field as decimal strings" $ do
let opts =
defaultSearchOptions
{ soMaxConcurrentShardRequests = Just 16,
soBatchedReduceSize = Just 512,
soPreFilterShardSize = Just 128,
soMaxConcurrentSearches = Just 4
}
normalize (searchOptionsParams opts SearchTypeQueryThenFetch)
`shouldBe` [ ("batched_reduce_size", Just "512"),
("max_concurrent_searches", Just "4"),
("max_concurrent_shard_requests", Just "16"),
("pre_filter_shard_size", Just "128")
]
it "renders expand_wildcards as a comma-separated list of values" $ do
let opts =
defaultSearchOptions
{ soExpandWildcards = Just (ExpandWildcardsOpen :| [ExpandWildcardsClosed])
}
searchOptionsParams opts SearchTypeQueryThenFetch
`shouldBe` [("expand_wildcards", Just "open,closed")]
it "renders a single expand_wildcards value without a trailing comma" $ do
let opts =
defaultSearchOptions
{ soExpandWildcards = Just (ExpandWildcardsAll :| [])
}
searchOptionsParams opts SearchTypeQueryThenFetch
`shouldBe` [("expand_wildcards", Just "all")]
it "renders preference and routing verbatim" $ do
let opts =
defaultSearchOptions
{ soPreference = Just "_only_local",
soRouting = Just "route1,route2"
}
normalize (searchOptionsParams opts SearchTypeQueryThenFetch)
`shouldBe` [ ("preference", Just "_only_local"),
("routing", Just "route1,route2")
]
it "renders _source as a true/false string" $ do
let opts = defaultSearchOptions {soSource = Just False}
searchOptionsParams opts SearchTypeQueryThenFetch
`shouldBe` [("_source", Just "false")]
it "renders _source_includes and _source_excludes verbatim" $ do
let opts =
defaultSearchOptions
{ soSourceIncludes = Just "metadata.*,name",
soSourceExcludes = Just "internal.*"
}
normalize (searchOptionsParams opts SearchTypeQueryThenFetch)
`shouldBe` [ ("_source_excludes", Just "internal.*"),
("_source_includes", Just "metadata.*,name")
]
it "renders cancel_after_time_interval as <n><unit>" $ do
let opts =
defaultSearchOptions
{ soCancelAfterTimeInterval = Just (TimeUnitSeconds, 5)
}
searchOptionsParams opts SearchTypeQueryThenFetch
`shouldBe` [("cancel_after_time_interval", Just "5s")]
it "supports every TimeUnits suffix" $ do
let mk u = searchOptionsParams defaultSearchOptions {soCancelAfterTimeInterval = Just (u, 1)} SearchTypeQueryThenFetch
mk TimeUnitDays `shouldBe` [("cancel_after_time_interval", Just "1d")]
mk TimeUnitHours `shouldBe` [("cancel_after_time_interval", Just "1h")]
mk TimeUnitMinutes `shouldBe` [("cancel_after_time_interval", Just "1m")]
mk TimeUnitSeconds `shouldBe` [("cancel_after_time_interval", Just "1s")]
mk TimeUnitMilliseconds `shouldBe` [("cancel_after_time_interval", Just "1ms")]
mk TimeUnitMicroseconds `shouldBe` [("cancel_after_time_interval", Just "1micros")]
mk TimeUnitNanoseconds `shouldBe` [("cancel_after_time_interval", Just "1nanos")]
it "emits the full param set when every field is populated" $ do
let opts =
defaultSearchOptions
{ soAllowNoIndices = Just True,
soExpandWildcards = Just (ExpandWildcardsOpen :| []),
soIgnoreUnavailable = Just True,
soPreference = Just "_local",
soRouting = Just "r1",
soRequestCache = Just False,
soTypedKeys = Just True,
soSeqNoPrimaryTerm = Just True,
soMaxConcurrentShardRequests = Just 5,
soBatchedReduceSize = Just 128,
soCcsMinimizeRoundtrips = Just True,
soAllowPartialSearchResults = Just False,
soPreFilterShardSize = Just 128,
soCancelAfterTimeInterval = Just (TimeUnitSeconds, 3),
soMaxConcurrentSearches = Just 8,
soSource = Just True,
soSourceIncludes = Just "a,b",
soSourceExcludes = Just "c"
}
-- Use DfsQueryThenFetch to verify search_type is included exactly
-- once alongside the 18 SearchOptions keys, locking both the count
-- and the exact wire key set.
let rendered = searchOptionsParams opts SearchTypeDfsQueryThenFetch
length rendered `shouldBe` 19
rendered `shouldSatisfy` all (isJust . snd)
sort (map fst rendered)
`shouldBe` sort
[ "_source",
"_source_excludes",
"_source_includes",
"allow_no_indices",
"allow_partial_search_results",
"batched_reduce_size",
"cancel_after_time_interval",
"ccs_minimize_roundtrips",
"expand_wildcards",
"ignore_unavailable",
"max_concurrent_searches",
"max_concurrent_shard_requests",
"pre_filter_shard_size",
"preference",
"request_cache",
"routing",
"search_type",
"seq_no_primary_term",
"typed_keys"
]