bloodhound-1.0.0.0: tests/Test/ScrollSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.ScrollSpec (spec) where
import Data.Aeson
( decode,
eitherDecode,
encode,
)
import Data.Aeson.Types (parseEither, parseJSON)
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Either (isLeft)
import Data.Maybe (isJust)
import Database.Bloodhound.Common.Types
import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)
import Prelude
-- Sample Clear Scroll response body, as documented by both Elasticsearch
-- and OpenSearch:
-- https://www.elastic.co/guide/en/elasticsearch/reference/current/clear-scroll-api.html
-- https://docs.opensearch.org/latest/api-reference/scroll-search/
sampleClearScrollResponseBody :: LBS.ByteString
sampleClearScrollResponseBody =
LBS.pack
( unlines
[ "{",
" \"succeeded\": true,",
" \"num_freed\": 2",
"}"
]
)
spec :: Spec
spec =
describe "Clear Scroll API response" $ do
it "decodes a well-formed {succeeded,num_freed} body" $ do
let decoded = decode sampleClearScrollResponseBody :: Maybe ClearScrollResponse
decoded `shouldSatisfy` isJust
let Just r = decoded
clearScrollSucceeded r `shouldBe` True
clearScrollNumFreed r `shouldBe` 2
it "round-trips decode -> encode -> decode" $ do
let Just (r1 :: ClearScrollResponse) = decode sampleClearScrollResponseBody
let reencoded = encode r1
decode reencoded `shouldBe` Just r1
it "rejects a body missing num_freed" $ do
let malformed =
LBS.pack
( unlines
[ "{",
" \"succeeded\": true",
"}"
]
)
let result = parseEither parseJSON =<< eitherDecode malformed :: Either String ClearScrollResponse
result `shouldSatisfy` isLeft
it "rejects a body missing succeeded" $ do
let malformed =
LBS.pack
( unlines
[ "{",
" \"num_freed\": 1",
"}"
]
)
let result = parseEither parseJSON =<< eitherDecode malformed :: Either String ClearScrollResponse
result `shouldSatisfy` isLeft
it "rejects a non-object body" $ do
let result = parseEither parseJSON =<< eitherDecode "[1,2,3]" :: Either String ClearScrollResponse
result `shouldSatisfy` isLeft
describe "advanceScrollOptionsParams rendering" $ do
-- The renderer is the wire contract for the new @?rest_total_hits_as_int@
-- parameter on @POST /_search/scroll@. These tests pin its output so
-- regressions are caught even without an integration test.
it "defaultAdvanceScrollOptions renders no parameters" $ do
advanceScrollOptionsParams defaultAdvanceScrollOptions
`shouldBe` []
it "asoRestTotalHitsAsInt = Just True renders rest_total_hits_as_int=true" $ do
let opts = defaultAdvanceScrollOptions {asoRestTotalHitsAsInt = Just True}
advanceScrollOptionsParams opts
`shouldBe` [("rest_total_hits_as_int", Just "true")]
it "asoRestTotalHitsAsInt = Just False renders rest_total_hits_as_int=false" $ do
let opts = defaultAdvanceScrollOptions {asoRestTotalHitsAsInt = Just False}
advanceScrollOptionsParams opts
`shouldBe` [("rest_total_hits_as_int", Just "false")]
describe "HitsTotal parses both wire shapes" $ do
-- The server emits the object form by default and the bare-integer
-- form when the client passes @?rest_total_hits_as_int=true@. Both
-- must decode into the same 'HitsTotal' value for the option to be
-- usable.
it "decodes the object form {value, relation}" $ do
let decoded = decode "{ \"value\": 5, \"relation\": \"eq\" }" :: Maybe HitsTotal
decoded `shouldBe` Just (HitsTotal 5 HTR_EQ)
it "decodes a bare integer as value with relation=eq" $ do
let decoded = decode "5" :: Maybe HitsTotal
decoded `shouldBe` Just (HitsTotal 5 HTR_EQ)
it "rejects a non-numeric, non-object scalar" $ do
let result = parseEither parseJSON =<< eitherDecode "\"five\"" :: Either String HitsTotal
result `shouldSatisfy` isLeft