@@ -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+```