hyperbole-0.7.0: test/Test/QuerySpec.hs
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE RecordWildCards #-}
module Test.QuerySpec where
import Data.Function ((&))
import Data.Text (Text)
import Skeletest
import Skeletest.Predicate qualified as P
import Web.Hyperbole hiding (Number)
import Web.Hyperbole.Data.QueryData as QueryData
spec :: Spec
spec = withMarkers ["param"] $ do
describe "render" renderSpec
describe "class" classSpec
describe "multi" multiSpec
data Woot = Woot Text
deriving (Generic, Show)
data Record = Record
{ age :: Int
, msg :: Text
}
deriving (Generic, ToJSON, FromJSON, Eq, FromQuery, ToQuery)
instance Default Record where
def = Record 0 ""
classSpec :: Spec
classSpec = do
describe "FromQuery" $ do
it "decodes record" $ do
let qd = QueryData.parse "age=20&msg=hello_world"
parseQuery @Record qd `shouldSatisfy` P.right P.anything
it "decodes numbers as text if needed" $ do
let qd = QueryData.parse "age=20&msg=30"
parseQuery @Record qd `shouldBe` Right (Record 20 "30")
describe "ToQuery" $ do
it "encodes record" $ do
let r = Record 20 "hello world_go+big"
QueryData.render (toQuery r) `shouldBe` "age=20&msg=hello+world_go%2Bbig"
describe "roundtrip" $ do
it "round trips" $ do
let r = Record 20 "hello world_go+big"
parseQuery (toQuery r) `shouldBe` Right r
renderSpec :: Spec
renderSpec = do
it "should parse multiple items" $ do
let qd = parse "msg=hello&age=1"
require @Text "msg" qd `shouldBe` Right "hello"
require @Int "age" qd `shouldBe` Right 1
it "should render as a querystring" $ do
let q =
mempty
& QueryData.insert @Text "msg" "value"
& QueryData.insert @Int "age" 1
QueryData.render q `shouldBe` "age=1&msg=value"
it "should escape special characters in strings" $ do
let q = mempty & QueryData.insert @Text "msg" "bob&henry=fast"
QueryData.render q `shouldBe` "msg=bob%26henry%3Dfast"
it "should roundtrip spaces" $ do
let msg = "hello world"
let q = mempty & QueryData.insert @Text "msg" msg
let out = QueryData.render q
let q' = QueryData.parse out
QueryData.lookup "msg" q' `shouldBe` Just msg
it "should roundtrip special characters" $ do
let msg = "bob&henry=fast"
let q = mempty & QueryData.insert @Text "msg" msg
let out = QueryData.render q
let q' = QueryData.parse out
QueryData.lookup "msg" q' `shouldBe` Just msg
data Filters = Filters
{ term :: Text
, isActive :: Bool
, another :: Maybe Text
}
deriving (Generic, Eq, Show)
instance ToQuery Filters where
toQuery f =
mempty
& QueryData.insert "term" f.term
& QueryData.insert "isActive" f.isActive
& QueryData.insert "another" f.another
instance FromQuery Filters where
parseQuery q = do
term <- QueryData.require "term" q
isActive <- QueryData.require "isActive" q
another <- QueryData.require "another" q
pure Filters{..}
data Nested = Nested
{ record :: Record
, awesome :: Bool
}
deriving (Generic, ToQuery, FromQuery, Eq)
multiSpec :: Spec
multiSpec = do
describe "Roundtrip" $ do
it "should parse from querydata" $ do
let f = Filters "hello world" False Nothing
roundTrip f
it "should work with Just" $ do
let f = Filters "hello_world" False (Just "hello")
roundTrip f
it "integers, special characters, spaces" $ do
let r = Record 30 "hello world_this+is"
roundTrip r
it "nested JSON records" $ do
let r = Record 30 "hello world_this+is"
let n = Nested r True
roundTrip n
where
roundTrip val = do
let out = QueryData.render (toQuery val)
let q = QueryData.parse out
parseQuery q `shouldBe` Right val