on master help sign in
wip number whatever
commit77f565b993258982e4c6f61baff964b24bf157da authorMark Wotton <mwotton@gmail.com> dateMon 24 Aug 2020, 17:30 (6 years ago) parenta75a9d1
4 files changed+121−7
package.yaml+3 roboservant.cabal+7−1 src/Roboservant/StateMachine.hs+109−5 test/Spec.hs+2−1
package.yaml+3
@@ -39,6 +39,9 @@ dependencies:
3939# test deps
4040- hspec
4141- QuickCheck
42+- warp
43+- aeson
44+- wai
4245library:
4346 source-dirs: src
4447
roboservant.cabal+7−1
@@ -4,7 +4,7 @@ cabal-version: 1.12
44--
55-- see: https://github.com/sol/hpack
66--
7−-- hash: a0b4b884d775a2096c422d6abaf66301ece323e26ea570df76ffc92b15ab89ba
7+-- hash: bc1746c1053b1f6bf66cc30046bb205fefd129303feff615f0d885999eace809
88
99name: roboservant
1010version: 0.1.0.0
@@ -37,6 +37,7 @@ library
3737 src
3838 build-depends:
3939 QuickCheck
40+ , aeson
4041 , base >=4.7 && <5
4142 , bytestring
4243 , constrained-dynamic
@@ -51,6 +52,8 @@ library
5152 , servant-flatten
5253 , servant-server >=0.17
5354 , string-conversions
55+ , wai
56+ , warp
5457 default-language: Haskell2010
5558
5659test-suite roboservant-test
@@ -63,6 +66,7 @@ test-suite roboservant-test
6366 ghc-options: -threaded -rtsopts -with-rtsopts=-N
6467 build-depends:
6568 QuickCheck
69+ , aeson
6670 , base >=4.7 && <5
6771 , bytestring
6872 , constrained-dynamic
@@ -78,4 +82,6 @@ test-suite roboservant-test
7882 , servant-flatten
7983 , servant-server >=0.17
8084 , string-conversions
85+ , wai
86+ , warp
8187 default-language: Haskell2010
src/Roboservant/StateMachine.hs+109−5
@@ -1,3 +1,7 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE DeriveAnyClass #-}
4+{-# LANGUAGE OverloadedStrings #-}
15{-# LANGUAGE LambdaCase #-}
26{-# LANGUAGE AllowAmbiguousTypes #-}
37{-# LANGUAGE DataKinds #-}
@@ -10,6 +14,11 @@
1014{-# LANGUAGE RecordWildCards #-}
1115{-# LANGUAGE StandaloneDeriving #-}
1216{-# LANGUAGE TupleSections #-}
17+
18+-- for servant
19+{-# LANGUAGE TypeOperators #-}
20+{-# LANGUAGE DeriveGeneric #-}
21+{-# LANGUAGE ScopedTypeVariables #-}
1322module Roboservant.StateMachine where
1423
1524import Data.Maybe
@@ -20,12 +29,14 @@ import Data.IORef (IORef)
2029-- import Data.Dynamic (Dynamic, fromDynamic)
2130import Data.Map.Strict (Map)
2231import qualified Data.Map.Strict as Map
23−import Data.Typeable (TypeRep, Typeable, typeRep)
32+import Data.Typeable (typeOf, TypeRep, Typeable, typeRep)
2433import Roboservant.ContextualGenRequest
2534import Servant.Server (Server)
2635--import Test.QuickCheck (Property)
2736-- import Test.StateMachine
2837
38+import Hedgehog
39+
2940import qualified Data.List.NonEmpty as NEL
3041import Data.List.NonEmpty(NonEmpty)
3142import Data.Dynamic
@@ -35,7 +46,16 @@ import Hedgehog
3546import qualified Hedgehog.Gen as Gen
3647import Control.Monad ((<=<))
3748import GHC.IORef (readIORef)
49+import qualified Hedgehog.Range as Range
3850
51+import Servant
52+import GHC.Generics(Generic)
53+import Data.Aeson
54+import GHC.Generics
55+import Network.Wai
56+import Network.Wai.Handler.Warp
57+import Control.Concurrent(forkIO)
58+import Network.HTTP.Client(newManager, defaultManagerSettings)
3959
4060-- | can then use either `forAllParallelCommands` or `forAllCommands` to turn
4161-- this into a property
@@ -131,10 +151,8 @@ callEndpoint staticRoutes env =
131151 -- function and hopefully somtehing useful pops out the end.
132152 func = foldr ( \arg curr -> dynApply arg =<< curr ) (Just endpoint) realArgs
133153 case func >>= fromDynamic of
134− Nothing -> error "all screwed up"
135− Just f -> runClientM f env >>= either (error . show) (pure . toDyn)
136−
137− undefined -- case fromDynamic
154+ Nothing -> error ("all screwed up: " <> maybe "nothing" (show . dynTypeRep) func)
155+ Just (f :: ClientM a) -> runClientM f env >>= either (error . show) pure
138156
139157 in Command gen execute
140158 [ Update $ \s@State{..} (Op (ApiOffset offset) args) o ->
@@ -143,3 +161,89 @@ callEndpoint staticRoutes env =
143161 }
144162 -- , Ensure (no-500 here)
145163 ]
164+
165+
166+
167+prop_sm_sequential :: ReifiedApi -> ClientEnv -> Property
168+prop_sm_sequential reifiedApi clientEnv = do
169+ property $ do
170+ let
171+ initialState = State mempty clientEnv
172+ actions <- forAll $ Gen.sequential (Range.linear 1 100) initialState
173+ [callEndpoint reifiedApi clientEnv]
174+ executeSequential initialState actions
175+
176+
177+
178+newtype Foo = Foo Int
179+ deriving (Generic, Eq, Show)
180+ deriving newtype (FromHttpApiData,ToHttpApiData)
181+
182+instance ToJSON Foo
183+instance FromJSON Foo
184+
185+type FooApi
186+ = "item" :> Get '[JSON] Foo
187+ :<|> "itemAdd" :> Capture "one" Foo :> Capture "two" Foo :> Get '[JSON] Foo
188+ :<|> "item" :> Capture "itemId" Foo :> Get '[JSON] ()
189+
190+
191+intro = pure (Foo 1)
192+combine = (\(Foo a) (Foo b) -> pure (Foo (a+b)))
193+eliminate = (\(Foo a) -> if a > 10
194+ then error "fuck"
195+ else pure ())
196+
197+fooServer :: Server FooApi
198+fooServer = intro
199+ :<|> combine
200+ :<|> eliminate
201+
202+
203+runServer :: IO ()
204+runServer = do
205+ let port = 3000
206+ settings =
207+ setPort port $
208+ setBeforeMainLoop (putStrLn ("listening on port " ++ show port)) $
209+ defaultSettings
210+ runSettings settings (serve fooApi fooServer)
211+
212+fooApi :: Proxy FooApi
213+fooApi = Proxy
214+
215+introC :<|> combineC :<|> eliminateC = Servant.Client.client fooApi
216+
217+tests :: IO Bool
218+tests = do
219+ withServantServer fooApi (pure fooServer) $ \burl -> do
220+ manager <- newManager defaultManagerSettings
221+ let clientEnv = mkClientEnv manager burl
222+ -- faking this out for now.
223+ footype = typeOf Foo
224+ reifiedApi = [(ApiOffset 0, [],footype,toDyn introC)
225+ ,(ApiOffset 1, [footype, footype],footype,toDyn combineC)
226+ ,(ApiOffset 2, [footype],(typeOf ()),toDyn eliminateC)]
227+
228+ -- type ReifiedApi = [(ApiOffset, [TypeRep], TypeRep, MyDyn)]
229+ checkParallel $ Group "props" [("aprop", prop_sm_sequential reifiedApi clientEnv )]
230+
231+
232+
233+-- | Start a servant application on an open port, run the provided function,
234+-- then stop the application.
235+--
236+-- /Since 0.0.0.0/
237+withServantServer :: HasServer a '[] => Proxy a -> IO (Server a)
238+ -> (BaseUrl -> IO r) -> IO r
239+withServantServer api = withServantServerAndContext api EmptyContext
240+
241+-- | Like 'withServantServer', but allows passing in a 'Context' to the
242+-- application.
243+--
244+-- /Since 0.0.0.0/
245+withServantServerAndContext :: (HasServer a ctx)
246+ => Proxy a -> Context ctx -> IO (Server a) -> (BaseUrl -> IO r) -> IO r
247+withServantServerAndContext api ctx server t
248+ = withApplication (return . serveWithContext api ctx =<< server) $ \port ->
249+ t (BaseUrl Http "localhost" port "")
test/Spec.hs+2−1
@@ -17,6 +17,7 @@
1717import Roboservant
1818import Servant.API
1919import Test.Hspec
20+import qualified Roboservant.StateMachine as SM
2021
2122newtype Foo = Foo Int
2223 deriving (Show)
@@ -48,4 +49,4 @@ test' = blah
4849storeOfOurApi = storeOfApi @Api
4950
5051main :: IO ()
51−main = putStrLn "Test suite not yet implemented"
52+main = SM.tests >>= print