@@ -1 +0,0 @@
@@ -0,0 +1,112 @@
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 #-}
25+ deriving (Generic, Eq, Show, Typeable)
26+ deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
29+ deriving (Generic, Eq, Show, Typeable)
30+ deriving newtype (Hashable, FromHttpApiData, ToHttpApiData)
33+ "item" :> Get '[JSON] A
34+ :<|> "itemAdd" :> Capture "one" B :> Capture "two" B :> Get '[JSON] B
35+ :<|> "item" :> Capture "itemId" B :> Get '[JSON] ()
37+goodServer = server (pure $ A 0)
38+badServer = server (pure $ A 1)
40+server introduce = introduce :<|> combine :<|> eliminate
42+ combine (B i) (B j) = pure $ B (i + j)
44+ | i > 10 = error "give up, eleven is way too big and probably not even real"
45+ | otherwise = pure ()
48+Our tests would usually be in a separate file:
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.
59+ -- if we get to 1000 interactions with the api, call it quits.
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
64+ -- 0 to 100: fail tests if we hit less than this percentage of endpoints.
65+ , coverageThreshold = 0
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)
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.
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
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.
92+deriving via (Compound B) instance BuildFrom B
93+deriving via (Atom B) instance Breakdown B
99+finally some uninteresting utilities
102+serverFailure :: Maybe Report -> Bool
103+serverFailure c = case c of
105+ let RoboservantException{..} = rsException
106+ in failureReason /= NoPossibleMoves
109+isNothing x = case x of