@@ -21,6 +21,7 @@ import qualified Foo
21import qualified Headers
21import qualified Headers
22import qualified Nested
22import qualified Nested
23import qualified Post
23import qualified Post
24import qualified Product
25import qualified Product
25import qualified QueryParams
26import qualified QueryParams
26import qualified Roboservant as R
27import qualified Roboservant as R
@@ -36,6 +37,7 @@ import Servant.Client(ClientEnv, mkClientEnv, baseUrlPort, parseBaseUrl,HasClien
36import Network.Wai(Application)
37import Network.Wai(Application)
37import qualified Network.Wai.Handler.Warp as Warp
38import qualified Network.Wai.Handler.Warp as Warp
38import Network.HTTP.Client (newManager, defaultManagerSettings)
39import Network.HTTP.Client (newManager, defaultManagerSettings)
40+import Control.Monad((>=>))
40main :: IO ()
42main :: IO ()
41main = hspec spec
43main = hspec spec
@@ -46,15 +48,15 @@ fuzzBoth
46 HasClient ClientM a)
48 HasClient ClientM a)
47 => String -> Server a -> R.Config -> (Maybe R.Report -> IO ()) -> Spec
49 => String -> Server a -> R.Config -> (Maybe R.Report -> IO ()) -> Spec
48fuzzBoth name server config condition = do
50fuzzBoth name server config condition = do
49− it (name <> " via server") $ do
51+ it (name <> " via server") $
50 RS.fuzz @a server config >>= condition
52 RS.fuzz @a server config >>= condition
52− around (withServer (serve (Proxy :: Proxy a) server)) $ do
54+ around (withServer (serve (Proxy :: Proxy a) server)) $
53 it (name <> " via client") $ \(clientEnv::ClientEnv) -> do
55 it (name <> " via client") $ \(clientEnv::ClientEnv) -> do
54− RC.fuzz @a clientEnv config >>= condition
56+ RC.fuzz @a clientEnv config >>= condition
56withServer :: Application -> ActionWith ClientEnv -> IO ()
58withServer :: Application -> ActionWith ClientEnv -> IO ()
57−withServer app action = Warp.testWithApplication (pure app) (\p -> genClientEnv p >>= action)
59+withServer app action = Warp.testWithApplication (pure app) (genClientEnv >=> action)
58 where genClientEnv port = do
60 where genClientEnv port = do
59 baseUrl <- parseBaseUrl "http://localhost"
61 baseUrl <- parseBaseUrl "http://localhost"
60 manager <- newManager defaultManagerSettings
62 manager <- newManager defaultManagerSettings
@@ -73,9 +75,15 @@ spec = do
73 Just (R.InsufficientCoverage _) -> True
75 Just (R.InsufficientCoverage _) -> True
74 _ -> False
76 _ -> False
76− describe "posted body" $ do
78+ describe "posted body" $
77 fuzzBoth @Post.Api "passes a coverage check using a posted body" Post.server R.defaultConfig {R.coverageThreshold = 0.99}
79 fuzzBoth @Post.Api "passes a coverage check using a posted body" Post.server R.defaultConfig {R.coverageThreshold = 0.99}
78− (`shouldSatisfy` isNothing)
80+ (`shouldSatisfy` isNothing)
83+ describe "PUTted body" $
84+ fuzzBoth @Put.Api "passes a coverage check using a posted body" Put.server R.defaultConfig {R.coverageThreshold = 0.99}
85+ (`shouldSatisfy` isNothing)
80 describe "seeded" $ do
88 describe "seeded" $ do
81 let res = Seeded.Seed 1
89 let res = Seeded.Seed 1
@@ -83,31 +91,29 @@ spec = do
83 (R.defaultConfig {R.seed = [(toDyn res, hash res)]})
91 (R.defaultConfig {R.seed = [(toDyn res, hash res)]})
84 (`shouldSatisfy` isNothing)
92 (`shouldSatisfy` isNothing)
86− describe "Foo" $ do
94+ describe "Foo" $
87 fuzzBoth @Foo.Api "finds an error in a basic app" Foo.server R.defaultConfig (`shouldSatisfy` serverFailure)
95 fuzzBoth @Foo.Api "finds an error in a basic app" Foo.server R.defaultConfig (`shouldSatisfy` serverFailure)
89− describe "QueryParams" $ do
97+ describe "QueryParams" $
90 fuzzBoth @QueryParams.Api "can handle query params" QueryParams.server R.defaultConfig { R.seed = [R.hashedDyn (12::Int)] }
98 fuzzBoth @QueryParams.Api "can handle query params" QueryParams.server R.defaultConfig { R.seed = [R.hashedDyn (12::Int)] }
91− (`shouldSatisfy` isNothing)
99+ (`shouldSatisfy` isNothing)
93 describe "BuildFrom" $ do
101 describe "BuildFrom" $ do
94− describe "headers (and sum types)" $ do
102+ describe "headers (and sum types)" $
95 fuzzBoth @Headers.Api "should find a failure that's dependent on using header info" Headers.server R.defaultConfig
103 fuzzBoth @Headers.Api "should find a failure that's dependent on using header info" Headers.server R.defaultConfig
96− (`shouldSatisfy` serverFailure)
104+ (`shouldSatisfy` serverFailure)
97− describe "product types" $ do
105+ describe "product types" $
98 fuzzBoth @Product.Api "should find a failure that's dependent on creating a product" Product.server
106 fuzzBoth @Product.Api "should find a failure that's dependent on creating a product" Product.server
99− R.defaultConfig {R.seed = [R.hashedDyn 'a', R.hashedDyn (1 :: Int)]}
107+ R.defaultConfig {R.seed = [R.hashedDyn 'a', R.hashedDyn (1 :: Int)]}
100− (`shouldSatisfy` serverFailure)
108+ (`shouldSatisfy` serverFailure)
101 describe "Breakdown" $ do
109 describe "Breakdown" $ do
102 fuzzBoth @Breakdown.ProductApi "handles products" Breakdown.productServer R.defaultConfig
110 fuzzBoth @Breakdown.ProductApi "handles products" Breakdown.productServer R.defaultConfig
103 (`shouldSatisfy` serverFailure)
111 (`shouldSatisfy` serverFailure)
104 fuzzBoth @Breakdown.SumApi "handles sums" Breakdown.sumServer R.defaultConfig
112 fuzzBoth @Breakdown.SumApi "handles sums" Breakdown.sumServer R.defaultConfig
105 (`shouldSatisfy` serverFailure)
113 (`shouldSatisfy` serverFailure)
106− describe "flattening" $ do
114+ describe "flattening" $
107− -- we don't actually do much here, this is just here to document the appropriate response
108− -- if you get a type error with a nested api.
109 fuzzBoth @Nested.FlatApi "can handle nested apis" Nested.server R.defaultConfig {R.coverageThreshold = 0.99}
115 fuzzBoth @Nested.FlatApi "can handle nested apis" Nested.server R.defaultConfig {R.coverageThreshold = 0.99}
110− (`shouldSatisfy` isNothing)
116+ (`shouldSatisfy` isNothing)
112serverFailure :: Maybe R.Report -> Bool
118serverFailure :: Maybe R.Report -> Bool
113serverFailure = \case
119serverFailure = \case
@@ -131,9 +137,13 @@ deriving via (R.Atom Seeded.Seed) instance R.BuildFrom Seeded.Seed
131deriving via (R.Atom Void) instance R.BuildFrom Void
137deriving via (R.Atom Void) instance R.BuildFrom Void
133deriving via (R.Atom Post.FooPost) instance R.Breakdown Post.FooPost
139deriving via (R.Atom Post.FooPost) instance R.Breakdown Post.FooPost
135deriving via (R.Atom Post.FooPost) instance R.BuildFrom Post.FooPost
140deriving via (R.Atom Post.FooPost) instance R.BuildFrom Post.FooPost
142+deriving via (R.Atom Put.Foo) instance R.Breakdown Put.Foo
143+deriving via (R.Atom Put.Foo) instance R.BuildFrom Put.Foo
137deriving via (R.Compound Breakdown.Foo) instance R.Breakdown Breakdown.Foo
147deriving via (R.Compound Breakdown.Foo) instance R.Breakdown Breakdown.Foo
139deriving via (R.Compound Product.Foo) instance R.BuildFrom Product.Foo
149deriving via (R.Compound Product.Foo) instance R.BuildFrom Product.Foo
@@ -148,7 +158,7 @@ shouldFail =
150 { itemExample = \p a cb -> do
160 { itemExample = \p a cb -> do
151− r <- (itemExample i) p a cb
161+ r <- itemExample i p a cb
154 { resultStatus = case resultStatus r of
164 { resultStatus = case resultStatus r of