File: HttpApiDataSpec.hs

package info (click to toggle)
haskell-http-api-data 0.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 192 kB
  • sloc: haskell: 1,044; makefile: 9; ansic: 8
file content (133 lines) | stat: -rw-r--r-- 6,219 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{-# LANGUAGE ScopedTypeVariables #-}
module Web.Internal.HttpApiDataSpec (spec) where


import           Prelude                    ()
import           Prelude.Compat

import qualified Data.ByteString            as BS
import           Data.ByteString.Builder    (toLazyByteString)
import           Data.Char
import qualified Data.Fixed                 as F
import           Data.Int
import qualified Data.Text                  as T
import qualified Data.Text.Encoding         as TE
import qualified Data.Text.Lazy             as L
import           Data.Time.Compat
import           Data.Time.Calendar.Month.Compat (Month)
import           Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
import qualified Data.UUID.Types            as UUID
import           Data.Version
import           Data.Word
import           Web.Cookie                 (SetCookie, defaultSetCookie,
                                             setCookieName, setCookieValue)

import           Data.Proxy

import           Numeric.Natural

import           Test.Hspec
import           Test.Hspec.QuickCheck      (prop)
import           Test.QuickCheck

import           Web.Internal.HttpApiData

import           Web.Internal.TestInstances

(<=>) :: forall a b. (Show a, Show b, Eq a) => (a -> b) -> (b -> Either T.Text a) -> a -> Property
(f <=> g) x = counterexample
    (show lhs' ++ " : " ++ show lhs ++ " /= " ++ show rhs)
    (lhs == rhs)
  where
    lhs' = f x
    lhs = g lhs' :: Either T.Text a
    rhs = Right x :: Either T.Text a

encodedUrlPieceProp :: ToHttpApiData a => a -> Property
encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x)

encodedQueryParamProp :: ToHttpApiData a => a -> Property
encodedQueryParamProp x = toLazyByteString (toEncodedQueryParam (toQueryParam x)) === toLazyByteString (toEncodedQueryParam x)

-- | Check 'ToHttpApiData' and 'FromHttpApiData' compatibility
checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec
checkUrlPiece _ = checkUrlPiece' (arbitrary :: Gen a)

checkUrlPiece' :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a) => Gen a -> String -> Spec
checkUrlPiece' gen name = describe name $ do
    prop "toUrlPiece <=> parseUrlPiece" $ forAll gen (toUrlPiece <=> parseUrlPiece :: a -> Property)
    prop "toQueryParam <=> parseQueryParam" $ forAll gen (toQueryParam <=> parseQueryParam :: a -> Property)
    prop "toHeader <=> parseHeader" $ forAll gen (toHeader <=> parseHeader :: a -> Property)
    prop "toEncodedUrlPiece encodes correctly" $ forAll gen encodedUrlPieceProp
    prop "toEncodedQueryParam encodes correctly" $ forAll gen encodedQueryParamProp

-- | Check case insensitivity for @parseUrlPiece@.
checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
checkUrlPieceI _ = checkUrlPiece (Proxy :: Proxy (RandomCase a))

spec :: Spec
spec = do
  describe "Instances" $ do
    checkUrlPiece  (Proxy :: Proxy ())        "()"
    checkUrlPiece  (Proxy :: Proxy Char)      "Char"
    checkUrlPieceI (Proxy :: Proxy Bool)      "Bool"
    checkUrlPieceI (Proxy :: Proxy Ordering)  "Ordering"
    checkUrlPiece  (Proxy :: Proxy Int)       "Int"
    checkUrlPiece  (Proxy :: Proxy Int8)      "Int8"
    checkUrlPiece  (Proxy :: Proxy Int16)     "Int16"
    checkUrlPiece  (Proxy :: Proxy Int32)     "Int32"
    checkUrlPiece  (Proxy :: Proxy Int64)     "Int64"
    checkUrlPiece  (Proxy :: Proxy Integer)   "Integer"
    checkUrlPiece  (Proxy :: Proxy Word)      "Word"
    checkUrlPiece  (Proxy :: Proxy Word8)     "Word8"
    checkUrlPiece  (Proxy :: Proxy Word16)    "Word16"
    checkUrlPiece  (Proxy :: Proxy Word32)    "Word32"
    checkUrlPiece  (Proxy :: Proxy Word64)    "Word64"
    checkUrlPiece  (Proxy :: Proxy String)    "String"
    checkUrlPiece  (Proxy :: Proxy T.Text)    "Text.Strict"
    checkUrlPiece  (Proxy :: Proxy L.Text)    "Text.Lazy"
    checkUrlPiece  (Proxy :: Proxy Day)       "Day"
    checkUrlPiece  (Proxy :: Proxy TimeOfDay) "TimeOfDay"
    checkUrlPiece  (Proxy :: Proxy LocalTime) "LocalTime"
    checkUrlPiece  (Proxy :: Proxy ZonedTime) "ZonedTime"
    checkUrlPiece  (Proxy :: Proxy UTCTime)   "UTCTime"
    checkUrlPiece  (Proxy :: Proxy NominalDiffTime) "NominalDiffTime"
    checkUrlPiece  (Proxy :: Proxy DayOfWeek) "DayOfWeek"
    checkUrlPiece  (Proxy :: Proxy Quarter)   "Quarter"
    checkUrlPiece  (Proxy :: Proxy QuarterOfYear) "QuarterOfYear"
    checkUrlPiece  (Proxy :: Proxy Month)     "Month"
    checkUrlPiece  (Proxy :: Proxy DayOfWeek) "DayOfWeek"
    checkUrlPiece  (Proxy :: Proxy Version)   "Version"
    checkUrlPiece  (Proxy :: Proxy UUID.UUID) "UUID"
    checkUrlPiece' setCookieGen               "Cookie"

    checkUrlPiece  (Proxy :: Proxy F.Uni)   "Uni"
    checkUrlPiece  (Proxy :: Proxy F.Deci)  "Deci"
    checkUrlPiece  (Proxy :: Proxy F.Centi) "Centi"
    checkUrlPiece  (Proxy :: Proxy F.Milli) "Milli"
    checkUrlPiece  (Proxy :: Proxy F.Micro) "Micro"
    checkUrlPiece  (Proxy :: Proxy F.Nano)  "Nano"
    checkUrlPiece  (Proxy :: Proxy F.Pico)  "Pico"

    checkUrlPiece  (Proxy :: Proxy (Maybe String))            "Maybe String"
    checkUrlPieceI (Proxy :: Proxy (Maybe Integer))           "Maybe Integer"
    checkUrlPiece  (Proxy :: Proxy (Either Integer T.Text))   "Either Integer Text"
    checkUrlPieceI (Proxy :: Proxy (Either Version Day))      "Either Version Day"

    checkUrlPiece  (Proxy :: Proxy Natural)   "Natural"

  it "bad integers are rejected" $ do
    parseUrlPieceMaybe (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)

  it "bounds checking works" $ do
    parseUrlPieceMaybe (T.pack "256") `shouldBe` (Nothing :: Maybe Int8)
    parseUrlPieceMaybe (T.pack "-10") `shouldBe` (Nothing :: Maybe Word)

  it "invalid utf8 is handled" $ do
    parseHeaderMaybe (BS.pack [128]) `shouldBe` (Nothing :: Maybe T.Text)

setCookieGen :: Gen SetCookie
setCookieGen = do
    n <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
    v <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
    return $ defaultSetCookie { setCookieName = n, setCookieValue = v }