@@ -2,7 +2,7 @@
22
33Our api under test:
44
5−```haskell
5+``` haskell
66-- Obligatory fancy-types pragma tax
77{-# LANGUAGE DataKinds #-}
88{-# LANGUAGE TypeOperators #-}
@@ -20,6 +20,7 @@ import Servant
2020import GHC.Generics
2121import Data.Typeable
2222import Data.Hashable
23+import Data.Maybe(isNothing, isJust)
2324
2425newtype A = A Int
2526 deriving (Generic, Eq, Show, Typeable)
@@ -34,9 +35,7 @@ type Api =
3435 :<|> "itemAdd" :> Capture "one" B :> Capture "two" B :> Get '[JSON] B
3536 :<|> "item" :> Capture "itemId" B :> Get '[JSON] ()
3637
37−goodServer = server (pure $ A 0)
38−badServer = server (pure $ A 1)
39−
38+server :: Handler A -> Server Api
4039server introduce = introduce :<|> combine :<|> eliminate
4140 where
4241 combine (B i) (B j) = pure $ B (i + j)
@@ -45,36 +44,55 @@ server introduce = introduce :<|> combine :<|> eliminate
4544 | otherwise = pure ()
4645```
4746
47+We have a "good" server, that never generates anything other than a 0. This means repeated application of
48+the combination/addition rule can never bring us to the dangerous state of numbers larger than 100.
49+
50+``` haskell
51+goodServer, badServer :: Server Api
52+goodServer = server (pure $ A 0)
53+badServer = server (pure $ A 1)
54+```
55+
4856In the test file, we first define the tests: the faulty server should fail and the good server should pass.
4957
5058```haskell
59+main :: IO ()
60+main = hspec spec
61+
62+spec :: Spec
5163spec = describe "example" $ do
5264 it "good server should not fail" $ do
5365 fuzz @Api goodServer config
5466 >>= (`shouldSatisfy` isNothing)
5567 it "bad server should fail" $ do
5668 fuzz @Api badServer config
57− >>= (`shouldSatisfy` serverFailure)
69+ >>= (`shouldSatisfy` isJust)
70+```
71+
5872
73+We expect to be able to cover the whole api from our starting point, so let's set the coverage to 0.99.
74+There are other tweakable things in the config, like maximum runtime, reps,
75+per-request healthchecks, seeds, and verbose logging. Have a look at
76+Roboservant.Types.Config for details.
77+
78+``` haskell
79+config :: Config
5980config = defaultConfig
60− {
61− -- we expect to be able to cover the api from our starting point:
62− -- this will fail the test if we don't.
63− coverageThreshold = 0.99
81+ { coverageThreshold = 0.99
6482 }
65−
66−-- there are other tweakable things in the config, like maximum runtime, reps,
67−-- per-request healthchecks, seeds, and verbose logging. Have a look at
68−-- Roboservant.Types.Config for details.
6983```
7084
71−And unless we want to ship roboservant and all its dependencies to production, we also need
85+Unless we want to ship roboservant and all its dependencies to production, we also need
7286some orphan instances: because As are the only value we can get without
7387an input, we need to be able to break them down.
7488
75−```haskell
89+``` haskell
7690deriving via (Compound A) instance Breakdown A
77−-- if we wanted to assemble As from parts as well, we'd derive using Compound
91+```
92+
93+if we wanted to assemble As from parts as well, we'd derive using Compound, but in this case we don't care.
94+
95+``` haskell
7896deriving via (Atom A) instance BuildFrom A
7997
8098```
@@ -86,20 +104,3 @@ build it up from components.
86104deriving via (Compound B) instance BuildFrom B
87105deriving via (Atom B) instance Breakdown B
88106```
89−
90−finally some uninteresting utilities and the entrypoint
91−
92−```haskell
93−main = hspec spec
94−
95−serverFailure :: Maybe Report -> Bool
96−serverFailure c = case c of
97− Just Report{..} ->
98− let RoboservantException{..} = rsException
99− in failureReason /= NoPossibleMoves
100− _ -> False
101−
102−isNothing x = case x of
103− Nothing -> True
104− _ -> False
105−```