hyperbole-0.7.0: test/Test/ArgumentSpec.hs
module Test.ArgumentSpec where
import Data.Aeson as A
import Data.String.Conversions (cs)
import Data.Text (Text)
import GHC.Generics
import Skeletest
import Web.Hyperbole.Data.Argument
import Web.Hyperbole.HyperView.Input
spec :: Spec
spec = do
argumentSpec
inputSpec
data Tag = A | B
deriving (Generic, ToJSON, FromJSON, Eq, Show)
data Tag2 = T2 | Tag Text
deriving (Generic, ToJSON, FromJSON, Eq, Show)
data Record = Record
{ age :: Int
, msg :: Text
}
deriving (Generic, ToJSON, FromJSON, Eq)
argumentSpec :: Spec
argumentSpec = do
describe "ToArgument" $ do
it "encodes basics as JSON" $ do
encodeArgument @Text "hello" `shouldBe` "\"hello\""
encodeArgument @Int 23 `shouldBe` "23"
it "encodes simple constructors raw" $ do
encodeArgument A `shouldBe` "A"
encodeArgument B `shouldBe` "B"
it "encodes Maybe as JSON" $ do
encodeArgument @(Maybe Int) Nothing `shouldBe` "null"
encodeArgument @(Maybe Int) (Just 23) `shouldBe` "23"
encodeArgument @(Maybe Tag) (Just A) `shouldBe` "A"
encodeArgument @(Maybe Tag) Nothing `shouldBe` "null"
it "encodes complex constructors as products" $ do
encodeArgument T2 `shouldBe` "(T2)"
encodeArgument (Tag "hello world") `shouldBe` "(Tag \"hello world\")"
-- it "should encode lists with spaces = plusses" $ do
-- encodeArgument @[Int] [1, 2, 3] `shouldBe` ParamValue ("1+2+3")
-- encodeArgument @[Text] ["one", "two"] `shouldBe` ParamValue ("one+two")
-- encodeArgument @[Text] ["hello world", "friend"] `shouldBe` ParamValue ("hello%20world+friend")
it "should not escape text" $ do
encodeArgument @Text "hello world" `shouldBe` "\"hello world\""
encodeArgument @Text "hello_world" `shouldBe` "\"hello_world\""
encodeArgument @Text "hello+world" `shouldBe` "\"hello+world\""
it "encodes json" $ do
let r = Record 10 "hello world"
encodeArgument r `shouldBe` cs (A.encode r)
let r2 = Record 10 "hello_world"
encodeArgument r2 `shouldBe` cs (A.encode r2)
encodeArgument r2 `shouldBe` cs (A.encode r2)
describe "FromArgument" $ do
it "parses basics" $ do
decodeArgument @Text "hello" `shouldBe` Right "hello"
decodeArgument @Int "3" `shouldBe` Right 3
it "decodes json" $ do
let r2 = Record 10 "hello_world"
decodeArgument (cs $ A.encode r2) `shouldBe` Right r2
it "can decode numbers as text" $ do
decodeArgument @Text "\"30\"" `shouldBe` Right "30"
it "should not escape text" $ do
decodeArgument @Text "\"hello world\"" `shouldBe` Right "hello world"
decodeArgument @Text "\"hello_world\"" `shouldBe` Right "hello_world"
decodeArgument @Text "\"hello+world\"" `shouldBe` Right "hello+world"
describe "RoundTrip" $ do
it "round trips constructors" $ do
decodeArgument (encodeArgument A) `shouldBe` Right A
decodeArgument (encodeArgument B) `shouldBe` Right B
decodeArgument (encodeArgument T2) `shouldBe` Right T2
let t = Tag "woo hoo"
decodeArgument (encodeArgument t) `shouldBe` Right t
it "round trips Maybe" $ do
decodeArgument @(Maybe Int) (encodeArgument @(Maybe Int) Nothing) `shouldBe` Right Nothing
decodeArgument @(Maybe Int) (encodeArgument @(Maybe Int) (Just 23)) `shouldBe` Right (Just 23)
it "round trips Maybe Product" $ do
decodeArgument @(Maybe Tag) (encodeArgument @(Maybe Tag) Nothing) `shouldBe` Right Nothing
decodeArgument @(Maybe Tag) (encodeArgument @(Maybe Tag) (Just A)) `shouldBe` Right (Just A)
data LanguageFamily
= Functional
| ObjectOriented
deriving (Eq, Show, ToJSON, FromJSON, Ord, Generic, InputValue)
inputSpec :: Spec
inputSpec = do
describe "InputValue" $ do
it "encodes Maybe option" $ do
encodeArgument (Just ObjectOriented) `shouldBe` "ObjectOriented"
it "round trips Maybe option" $ do
let enc = encodeArgument @(Maybe LanguageFamily) Nothing
parseInputValue "ObjectOriented" `shouldBe` Right (Just ObjectOriented)
parseInputValue @(Maybe LanguageFamily) enc `shouldBe` Right Nothing