@@ -21,7 +21,9 @@ 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
26+import qualified QueryParams
25import qualified Roboservant as R
27import qualified Roboservant as R
26import qualified Roboservant.Server as RS
28import qualified Roboservant.Server as RS
27import qualified Roboservant.Client as RC
29import qualified Roboservant.Client as RC
@@ -35,26 +37,26 @@ import Servant.Client(ClientEnv, mkClientEnv, baseUrlPort, parseBaseUrl,HasClien
35import Network.Wai(Application)
37import Network.Wai(Application)
36import qualified Network.Wai.Handler.Warp as Warp
38import qualified Network.Wai.Handler.Warp as Warp
37import Network.HTTP.Client (newManager, defaultManagerSettings)
39import Network.HTTP.Client (newManager, defaultManagerSettings)
38−import Servant(Endpoints,HasServer)
40+import Control.Monad((>=>))
40main :: IO ()
42main :: IO ()
41main = hspec spec
43main = hspec spec
44− :: forall a .
46+ :: forall a .
45 (R.ToReifiedApi (Endpoints a), HasServer a '[], RS.FlattenServer a, RC.ToReifiedClientApi (Endpoints a), RC.FlattenClient a,
47 (R.ToReifiedApi (Endpoints a), HasServer a '[], RS.FlattenServer a, RC.ToReifiedClientApi (Endpoints a), RC.FlattenClient a,
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,37 +75,45 @@ 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}
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}
78− (`shouldSatisfy` isNothing)
85+ (`shouldSatisfy` isNothing)
80 describe "seeded" $ do
88 describe "seeded" $ do
81 let res = Seeded.Seed 1
89 let res = Seeded.Seed 1
82 shouldFail $ fuzzBoth @Seeded.Api "finds an error using information passed in" Seeded.server
90 shouldFail $ fuzzBoth @Seeded.Api "finds an error using information passed in" Seeded.server
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)
97+ describe "QueryParams" $
98+ fuzzBoth @QueryParams.Api "can handle query params" QueryParams.server R.defaultConfig { R.seed = [R.hashedDyn (12::Int)] }
99+ (`shouldSatisfy` isNothing)
89 describe "BuildFrom" $ do
101 describe "BuildFrom" $ do
90− describe "headers (and sum types)" $ do
102+ describe "headers (and sum types)" $
91 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
92− (`shouldSatisfy` serverFailure)
104+ (`shouldSatisfy` serverFailure)
93− describe "product types" $ do
105+ describe "product types" $
94− 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
95− R.defaultConfig {R.seed = [R.hashedDyn 'a', R.hashedDyn (1 :: Int)]}
107+ R.defaultConfig {R.seed = [R.hashedDyn 'a', R.hashedDyn (1 :: Int)]}
96− (`shouldSatisfy` serverFailure)
108+ (`shouldSatisfy` serverFailure)
97 describe "Breakdown" $ do
109 describe "Breakdown" $ do
98 fuzzBoth @Breakdown.ProductApi "handles products" Breakdown.productServer R.defaultConfig
110 fuzzBoth @Breakdown.ProductApi "handles products" Breakdown.productServer R.defaultConfig
99 (`shouldSatisfy` serverFailure)
111 (`shouldSatisfy` serverFailure)
100 fuzzBoth @Breakdown.SumApi "handles sums" Breakdown.sumServer R.defaultConfig
112 fuzzBoth @Breakdown.SumApi "handles sums" Breakdown.sumServer R.defaultConfig
101 (`shouldSatisfy` serverFailure)
113 (`shouldSatisfy` serverFailure)
102− describe "flattening" $ do
114+ describe "flattening" $
103− -- we don't actually do much here, this is just here to document the appropriate response
104− -- if you get a type error with a nested api.
105 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}
106− (`shouldSatisfy` isNothing)
116+ (`shouldSatisfy` isNothing)
108serverFailure :: Maybe R.Report -> Bool
118serverFailure :: Maybe R.Report -> Bool
109serverFailure = \case
119serverFailure = \case
@@ -127,9 +137,13 @@ deriving via (R.Atom Seeded.Seed) instance R.BuildFrom Seeded.Seed
127deriving via (R.Atom Void) instance R.BuildFrom Void
137deriving via (R.Atom Void) instance R.BuildFrom Void
129deriving via (R.Atom Post.FooPost) instance R.Breakdown Post.FooPost
139deriving via (R.Atom Post.FooPost) instance R.Breakdown Post.FooPost
131deriving 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
133deriving via (R.Compound Breakdown.Foo) instance R.Breakdown Breakdown.Foo
147deriving via (R.Compound Breakdown.Foo) instance R.Breakdown Breakdown.Foo
135deriving via (R.Compound Product.Foo) instance R.BuildFrom Product.Foo
149deriving via (R.Compound Product.Foo) instance R.BuildFrom Product.Foo
@@ -144,7 +158,7 @@ shouldFail =
146 { itemExample = \p a cb -> do
160 { itemExample = \p a cb -> do
147− r <- (itemExample i) p a cb
161+ r <- itemExample i p a cb
150 { resultStatus = case resultStatus r of
164 { resultStatus = case resultStatus r of