packages feed

bloodhound-1.0.0.0: tests/Test/MultiGetOptionsSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.MultiGetOptionsSpec (spec) where

import Data.List (sort)
import Data.Text qualified as T
import Database.Bloodhound.Common.Types
import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)

-- | Stable ordering for equality. The renderer preserves field-declaration
-- order but tests should compare the set of params, not the order.
normalize :: [(T.Text, Maybe T.Text)] -> [(T.Text, Maybe T.Text)]
normalize = sort

-- | Boolean helpers rendered as @true@/@false@ strings.
boolTrue :: Maybe T.Text
boolTrue = Just "true"

boolFalse :: Maybe T.Text
boolFalse = Just "false"

spec :: Spec
spec =
  describe "MultiGetOptions URI param rendering" $ do
    it "defaultMultiGetOptions emits no params" $
      multiGetOptionsParams defaultMultiGetOptions
        `shouldBe` []

    describe "_source" $ do
      it "Just True renders as _source=true" $ do
        let opts = defaultMultiGetOptions {mgoSource = Just True}
        multiGetOptionsParams opts `shouldBe` [("_source", boolTrue)]
      it "Just False renders as _source=false" $ do
        let opts = defaultMultiGetOptions {mgoSource = Just False}
        multiGetOptionsParams opts `shouldBe` [("_source", boolFalse)]

    it "_source_includes renders verbatim" $ do
      let opts = defaultMultiGetOptions {mgoSourceIncludes = Just "metadata.*,name"}
      multiGetOptionsParams opts
        `shouldBe` [("_source_includes", Just "metadata.*,name")]

    it "_source_excludes renders verbatim" $ do
      let opts = defaultMultiGetOptions {mgoSourceExcludes = Just "internal.*"}
      multiGetOptionsParams opts
        `shouldBe` [("_source_excludes", Just "internal.*")]

    it "stored_fields renders verbatim" $ do
      let opts = defaultMultiGetOptions {mgoStoredFields = Just "user,location"}
      multiGetOptionsParams opts
        `shouldBe` [("stored_fields", Just "user,location")]

    it "preference renders verbatim" $ do
      let opts = defaultMultiGetOptions {mgoPreference = Just "_local"}
      multiGetOptionsParams opts
        `shouldBe` [("preference", Just "_local")]

    describe "realtime" $ do
      it "Just True renders as realtime=true" $ do
        let opts = defaultMultiGetOptions {mgoRealtime = Just True}
        multiGetOptionsParams opts `shouldBe` [("realtime", boolTrue)]
      it "Just False renders as realtime=false" $ do
        let opts = defaultMultiGetOptions {mgoRealtime = Just False}
        multiGetOptionsParams opts `shouldBe` [("realtime", boolFalse)]

    describe "refresh" $ do
      it "Just True renders as refresh=true" $ do
        let opts = defaultMultiGetOptions {mgoRefresh = Just True}
        multiGetOptionsParams opts `shouldBe` [("refresh", boolTrue)]
      it "Just False renders as refresh=false" $ do
        let opts = defaultMultiGetOptions {mgoRefresh = Just False}
        multiGetOptionsParams opts `shouldBe` [("refresh", boolFalse)]

    it "routing renders verbatim" $ do
      let opts = defaultMultiGetOptions {mgoRouting = Just "user-42"}
      multiGetOptionsParams opts `shouldBe` [("routing", Just "user-42")]

    -- The whole point of the dedicated MultiGetOptions type (vs reusing
    -- GetDocumentOptions) is that the multi-get endpoint does NOT accept
    -- version/version_type as URI parameters. Pin the absence here so a
    -- future refactor cannot silently regress the wire-level bug.
    it "never emits version or version_type (not MGET URI params)" $ do
      let rendered = multiGetOptionsParams defaultMultiGetOptions
      let keys = map fst rendered
      keys `shouldSatisfy` not . elem "version"
      keys `shouldSatisfy` not . elem "version_type"

    it "renders every field together when all are populated" $ do
      let opts =
            defaultMultiGetOptions
              { mgoSource = Just False,
                mgoSourceIncludes = Just "a,b",
                mgoSourceExcludes = Just "c",
                mgoStoredFields = Just "s",
                mgoPreference = Just "_local",
                mgoRealtime = Just True,
                mgoRefresh = Just False,
                mgoRouting = Just "r"
              }
      let rendered = multiGetOptionsParams opts
      -- Exactly the 8 documented MGET URI parameters — no version*, no
      -- surprises.
      length rendered `shouldBe` 8
      rendered `shouldSatisfy` all (isJust . snd)
      normalize rendered
        `shouldBe` sort
          [ ("_source", Just "false"),
            ("_source_excludes", Just "c"),
            ("_source_includes", Just "a,b"),
            ("preference", Just "_local"),
            ("realtime", Just "true"),
            ("refresh", Just "false"),
            ("routing", Just "r"),
            ("stored_fields", Just "s")
          ]
  where
    isJust (Just _) = True
    isJust Nothing = False