on master help sign in
swap roles of lhs & md
18ca0a2Mark Wotton
EXAMPLE.mdmode 120000 → 100644+112−1
@@ -1 +0,0 @@
1−Example.lhs
@@ -0,0 +1,112 @@
1+# Example
2+
3+Our api under test:
4+
5+```haskell
6+-- Obligatory fancy-types pragma tax
7+{-# LANGUAGE DataKinds #-}
8+{-# LANGUAGE TypeOperators #-}
9+{-# LANGUAGE DerivingStrategies #-}
10+{-# LANGUAGE StandaloneDeriving #-}
11+{-# LANGUAGE DerivingVia #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE DeriveGeneric #-}
14+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
15+{-# LANGUAGE RecordWildCards #-}
16+
17+import Roboservant
18+import Test.Hspec
19+import Servant
20+import GHC.Generics
21+import Data.Typeable
22+import Data.Hashable
23+
24+newtype A = A Int
25+ deriving (Generic, Eq, Show, Typeable)
26+ deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
27+
28+newtype B = B Int
29+ deriving (Generic, Eq, Show, Typeable)
30+ deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
31+
32+type Api =
33+ "item" :> Get '[JSON] A
34+ :<|> "itemAdd" :> Capture "one" B :> Capture "two" B :> Get '[JSON] B
35+ :<|> "item" :> Capture "itemId" B :> Get '[JSON] ()
36+
37+goodServer = server (pure $ A 0)
38+badServer = server (pure $ A 1)
39+
40+server introduce = introduce :<|> combine :<|> eliminate
41+ where
42+ combine (B i) (B j) = pure $ B (i + j)
43+ eliminate (B i)
44+ | i > 10 = error "give up, eleven is way too big and probably not even real"
45+ | otherwise = pure ()
46+```
47+
48+Our tests would usually be in a separate file:
49+
50+```haskell
51+
52+
53+defaultConfig :: Config
54+defaultConfig = Config {
55+ -- you can pass extra values in using the seed argument. This can be useful
56+ -- for things that might not be produceable within the api, like auth tokens.
57+ seed = []
58+ , maxRuntime = 0.5
59+ -- if we get to 1000 interactions with the api, call it quits.
60+ , maxReps = 1000
61+ -- if you're using this inside quickcheck or hedgehog, you might want to set this
62+ -- from their seed to make sure it stays deterministic
63+ , rngSeed = 0
64+ -- 0 to 100: fail tests if we hit less than this percentage of endpoints.
65+ , coverageThreshold = 0
66+ }
67+
68+spec = describe "example" $ do
69+ it "good server should not fail" $ do
70+ fuzz @Api goodServer defaultConfig { coverageThreshold = 0.99 } (pure ())
71+ >>= (`shouldSatisfy` isNothing)
72+ it "bad server should fail" $ do
73+ fuzz @Api badServer defaultConfig { coverageThreshold = 0.99 } (pure ())
74+ >>= (`shouldSatisfy` serverFailure)
75+```
76+
77+And unless we want to ship roboservant and all its dependencies to production, we also need
78+some orphan instances: because As are the only value we can get without
79+an input, we need to be able to break them down.
80+
81+```haskell
82+deriving via (Compound A) instance Breakdown A
83+-- if we wanted to assemble As from parts as well, we'd derive using Compound
84+deriving via (Atom A) instance BuildFrom A
85+
86+```
87+
88+Similarly, to generate the first B from the Ints we got from inside the A, we need to be able to
89+build it up from components.
90+
91+```haskell
92+deriving via (Compound B) instance BuildFrom B
93+deriving via (Atom B) instance Breakdown B
94+
95+
96+main = hspec spec
97+```
98+
99+finally some uninteresting utilities
100+
101+```haskell
102+serverFailure :: Maybe Report -> Bool
103+serverFailure c = case c of
104+ Just Report{..} ->
105+ let RoboservantException{..} = rsException
106+ in failureReason /= NoPossibleMoves
107+ _ -> False
108+
109+isNothing x = case x of
110+ Nothing -> True
111+ _ -> False
112+```
Example.lhsmode 100644 → 120000+1−112
@@ -1,112 +0,0 @@
1−# Example
2−
3−Our api under test:
4−
5−```haskell
6−-- Obligatory fancy-types pragma tax
7−{-# LANGUAGE DataKinds #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE DerivingStrategies #-}
10−{-# LANGUAGE StandaloneDeriving #-}
11−{-# LANGUAGE DerivingVia #-}
12−{-# LANGUAGE TypeApplications #-}
13−{-# LANGUAGE DeriveGeneric #-}
14−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
15−{-# LANGUAGE RecordWildCards #-}
16−
17−import Roboservant
18−import Test.Hspec
19−import Servant
20−import GHC.Generics
21−import Data.Typeable
22−import Data.Hashable
23−
24−newtype A = A Int
25− deriving (Generic, Eq, Show, Typeable)
26− deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
27−
28−newtype B = B Int
29− deriving (Generic, Eq, Show, Typeable)
30− deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
31−
32−type Api =
33− "item" :> Get '[JSON] A
34− :<|> "itemAdd" :> Capture "one" B :> Capture "two" B :> Get '[JSON] B
35− :<|> "item" :> Capture "itemId" B :> Get '[JSON] ()
36−
37−goodServer = server (pure $ A 0)
38−badServer = server (pure $ A 1)
39−
40−server introduce = introduce :<|> combine :<|> eliminate
41− where
42− combine (B i) (B j) = pure $ B (i + j)
43− eliminate (B i)
44− | i > 10 = error "give up, eleven is way too big and probably not even real"
45− | otherwise = pure ()
46−```
47−
48−Our tests would usually be in a separate file:
49−
50−```haskell
51−
52−
53−defaultConfig :: Config
54−defaultConfig = Config {
55− -- you can pass extra values in using the seed argument. This can be useful
56− -- for things that might not be produceable within the api, like auth tokens.
57− seed = []
58− , maxRuntime = 0.5
59− -- if we get to 1000 interactions with the api, call it quits.
60− , maxReps = 1000
61− -- if you're using this inside quickcheck or hedgehog, you might want to set this
62− -- from their seed to make sure it stays deterministic
63− , rngSeed = 0
64− -- 0 to 100: fail tests if we hit less than this percentage of endpoints.
65− , coverageThreshold = 0
66− }
67−
68−spec = describe "example" $ do
69− it "good server should not fail" $ do
70− fuzz @Api goodServer defaultConfig { coverageThreshold = 0.99 } (pure ())
71− >>= (`shouldSatisfy` isNothing)
72− it "bad server should fail" $ do
73− fuzz @Api badServer defaultConfig { coverageThreshold = 0.99 } (pure ())
74− >>= (`shouldSatisfy` serverFailure)
75−```
76−
77−And unless we want to ship roboservant and all its dependencies to production, we also need
78−some orphan instances: because As are the only value we can get without
79−an input, we need to be able to break them down.
80−
81−```haskell
82−deriving via (Compound A) instance Breakdown A
83−-- if we wanted to assemble As from parts as well, we'd derive using Compound
84−deriving via (Atom A) instance BuildFrom A
85−
86−```
87−
88−Similarly, to generate the first B from the Ints we got from inside the A, we need to be able to
89−build it up from components.
90−
91−```haskell
92−deriving via (Compound B) instance BuildFrom B
93−deriving via (Atom B) instance Breakdown B
94−
95−
96−main = hspec spec
97−```
98−
99−finally some uninteresting utilities
100−
101−```haskell
102−serverFailure :: Maybe Report -> Bool
103−serverFailure c = case c of
104− Just Report{..} ->
105− let RoboservantException{..} = rsException
106− in failureReason /= NoPossibleMoves
107− _ -> False
108−
109−isNothing x = case x of
110− Nothing -> True
111− _ -> False
112−```
@@ -0,0 +1 @@
1+EXAMPLE.md