@@ -23,13 +23,15 @@ module Test.WebApi.StateModel
2323 , getSuccessHeaders
2424 , getSuccessCookies
2525 , defSuccessApiModel
26+ , defFailureApiModel
2627 , initApiState
2728 , modifyApiState
2829 ) where
2930
3031import Test.WebApi
3132import Test.QuickCheck.StateModel
3233import Test.QuickCheck.DynamicLogic (DynLogicModel (.. ))
34+ -- import Test.QuickCheck.StateModel.Variables (Any (..))
3335import qualified Test.QuickCheck as QC
3436import WebApi.Contract
3537import WebApi.Param
@@ -48,9 +50,7 @@ import Data.Functor.Identity
4850import qualified Data.Text as T
4951import qualified Data.Map.Strict as M
5052import qualified Data.Set as Set
51- import Data.Dependent.Map (DMap )
5253import Data.Dependent.Sum (DSum (.. ))
53- import qualified Data.Dependent.Map as DMap
5454import System.IO.Unsafe
5555import qualified Unsafe.Coerce as Unsafe
5656import qualified GHC.Base as Unsafe (Any )
@@ -110,7 +110,7 @@ data Val a where
110110 Const :: a -> Val a
111111 Var :: Typeable x => (x -> a ) -> Var x -> Val a
112112 HKVal :: (Typeable x , Record. FromHK x ) => (x -> a ) -> Record. HK Val x -> Val a -- TODO: Avoid FromHK dep
113- Pair :: ((x1 , x2 ) -> a ) -> (Val x1 , Val x2 ) -> Val a
113+ Pair :: (Typeable x1 , Typeable x2 ) => ( (x1 , x2 ) -> a ) -> (Val x1 , Val x2 ) -> Val a
114114 Opt :: Val a
115115
116116deriving instance Functor Val
@@ -136,20 +136,28 @@ instance Applicative Val where
136136 HKVal fn1 hkv1 -> Pair (\ (x, x1) -> fn x (fn1 x1)) (HKVal id v, HKVal id hkv1)
137137 Pair fn1 vs -> Pair (\ (x, x1) -> fn x (fn1 x1)) (HKVal id v, Pair id vs)
138138 Opt -> error " TODO"
139+ Pair fn vs -> case a' of
140+ Const a -> Pair ((\ f -> f a) . fn) vs
141+ Var fn1 v -> Pair (\ (x1x2, x) -> fn x1x2 (fn1 x)) (Pair id vs, Var id v)
142+ HKVal fn1 hkv1 -> Pair (\ (x1x2, x) -> fn x1x2 (fn1 x)) (Pair id vs, HKVal id hkv1)
143+ Pair fn1 vs1 -> Pair (\ (x1x2, x4x5) -> fn x1x2 (fn1 x4x5)) (Pair id vs, Pair id vs1)
144+ Opt -> error " TODO"
139145 Opt -> Opt
140146
141147resolveVal :: LookUp -> Val a -> a
142148resolveVal lkp = \ case
143149 Const v -> v
144150 Var f var -> f (lkp var)
145151 HKVal f hk -> f $ runIdentity $ Record. fromHK $ Record. hoistHK (Identity . resolveVal lkp) hk
152+ Pair f (v1, v2) -> f (resolveVal lkp v1, resolveVal lkp v2)
146153 Opt -> error " TODO"
147154
148155instance HasVariables (Val a ) where
149156 getAllVariables = \ case
150157 Const {} -> Set. empty
151158 Var _ var -> getAllVariables var
152159 HKVal _ hk -> Set. unions $ Record. hkToListWith getAllVariables hk
160+ Pair _ (v1, v2) -> getAllVariables v1 <> getAllVariables v2
153161 Opt -> error " TODO"
154162
155163data ClientRequestVal meth r = ClientRequestVal
@@ -181,19 +189,19 @@ resolveRequest ::
181189 -> Maybe (ClientRequest meth r )
182190resolveRequest lkp ClientRequestVal {query, form, header, path, file, body} =
183191 do
184- query <- Just $ resolveVal lkp query
185- form <- Just $ resolveVal lkp form
186- header <- Just $ resolveVal lkp header
187- path <- Just $ resolveVal lkp path
188- file <- Just $ resolveVal lkp file
189- body <- Just $ resolveVal lkp body
190- Just $ ClientRequest {query, form, header, path, file, body}
192+ query' <- Just $ resolveVal lkp query
193+ form' <- Just $ resolveVal lkp form
194+ header' <- Just $ resolveVal lkp header
195+ path' <- Just $ resolveVal lkp path
196+ file' <- Just $ resolveVal lkp file
197+ body' <- Just $ resolveVal lkp body
198+ Just $ ClientRequest {query = query' , form = form' , header = header' , path = path' , file = file' , body = body' }
191199
192200data WebApiAction s (apps :: [Type ]) (a :: Type ) where
193201 SuccessCall :: WebApiActionCxt apps meth app r
194202 => ClientRequestVal meth (app :// r )
195203 -> SuccessApiModel s apps meth (app :// r ) res
196- -> ModifyClientCookies
204+ -> Maybe ( ApiSuccess meth ( app :// r ) -> ModifyClientCookies app )
197205 -> (ApiSuccess meth (app :// r ) -> Either ResultError res )
198206 -> WebApiAction s apps res
199207 ErrorCall :: WebApiActionCxt apps meth app r
@@ -224,6 +232,8 @@ instance Eq (WebApiAction s apps a) where
224232instance HasVariables (WebApiAction s apps a ) where
225233 getAllVariables = \ case
226234 SuccessCall creq _ _ _ -> getAllVariables creq
235+ ErrorCall _creq -> error " TODO:"
236+ SomeExceptionCall _creq -> error " TODO:"
227237
228238newtype RefinementId = RefinementId Text
229239 deriving newtype (Show , Eq , Ord , Read )
@@ -234,6 +244,15 @@ data NamedEntity k = NamedEntity
234244 , entityToRefinements :: M. Map k (Set. Set RefinementId )
235245 }
236246
247+ instance Show k => Show (NamedEntity k ) where
248+ show NamedEntity {namedEntity = ne, entityRefinement = er, entityToRefinements = e2r} =
249+ " Entities: " ++ show (M. keys ne) ++ " , Refinements: " ++ (show $ M. keys er) ++ " , EntityRefinements: " ++ show e2r
250+
251+ instance Eq k => Eq (NamedEntity k ) where
252+ NamedEntity {namedEntity = ne1, entityRefinement = er1, entityToRefinements = e2r1} ==
253+ NamedEntity {namedEntity = ne2, entityRefinement = er2, entityToRefinements = e2r2} =
254+ ne1 == ne2 && (M. keys er1) == (M. keys er2) && e2r1 == e2r2
255+
237256instance Ord k => Semigroup (NamedEntity k ) where
238257 NamedEntity {namedEntity = ne1, entityRefinement = er1, entityToRefinements = e2r1} <>
239258 NamedEntity {namedEntity = ne2, entityRefinement = er2, entityToRefinements = e2r2} =
@@ -250,7 +269,7 @@ instance Ord k => Monoid (NamedEntity k) where
250269 }
251270
252271newtype NamedEntityTyped = NamedEntityTyped (NamedEntity TypeRep )
253- deriving newtype (Semigroup , Monoid )
272+ deriving newtype (Semigroup , Monoid , Show , Eq )
254273
255274newtype KeyedEntityId = KeyedEntityId RefinementId
256275 deriving newtype (Show , Eq , Ord , Read )
@@ -259,13 +278,13 @@ data NamedEntityKeyed = NamedEntityKeyed
259278 { keyToName :: M. Map Trail KeyedEntityId
260279 , typeOfEntities :: M. Map KeyedEntityId TypeRep
261280 , namedEntityKeyed :: NamedEntity KeyedEntityId
262- }
281+ } deriving ( Show , Eq )
263282
264283instance Semigroup NamedEntityKeyed where
265284 NamedEntityKeyed {keyToName = k2n1, typeOfEntities = tys1, namedEntityKeyed = ne1} <>
266285 NamedEntityKeyed {keyToName = k2n2, typeOfEntities = tys2, namedEntityKeyed = ne2} =
267- NamedEntityKeyed { keyToName = M. unionWith (\ n o -> error " " ) k2n1 k2n2
268- , typeOfEntities = M. unionWith (\ n o -> error " " ) tys1 tys2
286+ NamedEntityKeyed { keyToName = M. unionWithKey (\ k l r -> if l == r then l else error $ " Encountered duplicate trail: " ++ show k ++ " ! Pointing to conflicting entities: " ++ show (l, r) ) k2n1 k2n2
287+ , typeOfEntities = M. unionWithKey (\ k l r -> if l == r then l else error $ " Encountered duplicate entity: " ++ show k ++ " ! with conflicting type: " ++ show (l, r) ) tys1 tys2
269288 , namedEntityKeyed = ne1 <> ne2
270289 }
271290
@@ -285,18 +304,21 @@ data ApiState (s :: Type) (apps :: [Type]) = ApiState
285304 }
286305
287306instance Show (ApiState s apps ) where
288- show (ApiState {apiState}) = " undefined" -- showTaggedPrec p $DMap.dmap
307+ show (ApiState {apiState, namedEntityTyped, namedEntityKeyed}) =
308+ " ApiState: " ++ show (M. keys apiState) ++ " , Entites (Typed): " ++ show namedEntityTyped ++ " , Entites (Keyed): " ++ show namedEntityKeyed
289309
290310instance Eq (ApiState s apps ) where
291- s1 == s2 = undefined
311+ ApiState {namedEntityTyped = net1, namedEntityKeyed = nek1} ==
312+ ApiState {namedEntityTyped = net2, namedEntityKeyed = nek2} = net1 == net2 && nek1 == nek2
292313
293314modifyApiState :: forall app apps stTag s . (Typeable app , AppIsElem app apps ) => DSum (stTag apps app ) Proxy -> (DSum (stTag apps app ) Identity -> DSum (stTag apps app ) Identity ) -> ApiState s apps -> ApiState s apps
294- modifyApiState ctor@ (tag :=> _) f (ApiState {apiState = stMap, namedEntityTyped}) = case M. lookup (typeRep (getAppProxy' ctor)) stMap of
315+ modifyApiState ctor@ (tag :=> _) f (ApiState {apiState = stMap, namedEntityTyped, namedEntityKeyed }) = case M. lookup (typeRep (getAppProxy' ctor)) stMap of
295316 Nothing -> undefined
296317 Just anyv -> case f (tag :=> (Identity $ castToTagVal tag anyv)) of
297318 _ :=> (Identity newval) -> ApiState
298319 { apiState = M. insert (typeRep (getAppProxy' ctor)) (Unsafe. unsafeCoerce newval :: Unsafe. Any ) stMap
299320 , namedEntityTyped
321+ , namedEntityKeyed
300322 }
301323 where
302324 castToTagVal :: forall tag x . tag x -> Unsafe. Any -> x
@@ -307,7 +329,9 @@ class HasApiState (apps1 :: [Type]) stTag (apps :: [Type]) where
307329
308330initApiState :: forall apps stTag s . HasApiState apps stTag apps => (forall app . Typeable app => DSum (stTag apps app ) Proxy -> DSum (stTag apps app ) Identity ) -> ApiState s apps
309331initApiState f = ApiState { apiState = M. fromList $ apiStateUniv (Proxy @ apps ) $ \ ctor -> case f ctor of
310- tag :=> (Identity v) -> (typeRep (getAppProxy' ctor), Unsafe. unsafeCoerce v :: Unsafe. Any )
332+ _ :=> (Identity v) -> (typeRep (getAppProxy' ctor), Unsafe. unsafeCoerce v :: Unsafe. Any )
333+ , namedEntityTyped = mempty
334+ , namedEntityKeyed = mempty
311335 }
312336
313337
@@ -335,6 +359,8 @@ defSuccessApiModel = SuccessApiModel
335359 , precondition = Nothing
336360 , validFailingAction = Nothing
337361 , shrinkAction = Nothing
362+ , postCondition = \ _ _ _ -> True
363+ , postconditionOnFailure = \ _ _ _ -> True
338364 }
339365
340366data FailureApiModel s apps meth r a = FailureApiModel
@@ -345,6 +371,15 @@ data FailureApiModel s apps meth r a = FailureApiModel
345371 , postconditionOnFailure :: (ApiState s apps , ApiState s apps ) -> LookUp -> Either ErrorState a -> Bool
346372 }
347373
374+ defFailureApiModel :: FailureApiModel s apps meth r a
375+ defFailureApiModel = FailureApiModel
376+ { failureNextState = Nothing
377+ , precondition = Nothing
378+ , validFailingAction = Nothing
379+ , shrinkAction = Nothing
380+ , postconditionOnFailure = \ _ _ _ -> True
381+ }
382+
348383mkWebApiAction :: WebApiAction s apps a -> Action (ApiState s apps ) a
349384mkWebApiAction = coerce
350385
@@ -374,19 +409,29 @@ instance (Reifies s (WebApiGlobalStateModel apps)) => StateModel (ApiState s app
374409 in ApiState {apiState = coerce appInitState, namedEntityTyped, namedEntityKeyed}
375410
376411 nextState s (MkWebApiAction act) var = case act of
377- SuccessCall creq SuccessApiModel {nextState= nsMay} _ _ -> maybe s (\ ns -> ns var s) nsMay
412+ SuccessCall _creq SuccessApiModel {nextState= nsMay} _ _ -> maybe s (\ ns -> ns var s) nsMay
413+ ErrorCall {} -> error " TODO:"
414+ SomeExceptionCall {} -> error " TODO:"
378415
379416 failureNextState s (MkWebApiAction act) = case act of
380- SuccessCall creq SuccessApiModel {failureNextState= nsMay} _ _ -> maybe s (\ ns -> ns s) nsMay
417+ SuccessCall _creq SuccessApiModel {failureNextState= nsMay} _ _ -> maybe s (\ ns -> ns s) nsMay
418+ ErrorCall {} -> error " TODO:"
419+ SomeExceptionCall {} -> error " TODO:"
381420
382421 precondition s (MkWebApiAction act) = case act of
383- SuccessCall creq SuccessApiModel {precondition= pcMay} _ _ -> maybe True (\ pc -> pc s) pcMay
422+ SuccessCall _creq SuccessApiModel {precondition= pcMay} _ _ -> maybe True (\ pc -> pc s) pcMay
423+ ErrorCall {} -> error " TODO:"
424+ SomeExceptionCall {} -> error " TODO:"
384425
385426 validFailingAction s (MkWebApiAction act) = case act of
386- SuccessCall creq SuccessApiModel {validFailingAction= vfaMay} _ _ -> maybe False (\ vfa -> vfa s) vfaMay
427+ SuccessCall _creq SuccessApiModel {validFailingAction= vfaMay} _ _ -> maybe False (\ vfa -> vfa s) vfaMay
428+ ErrorCall {} -> error " TODO:"
429+ SomeExceptionCall {} -> error " TODO:"
387430
388431 shrinkAction varCxt s (MkWebApiAction act) = case act of
389- SuccessCall creq SuccessApiModel {shrinkAction= saMay} _ _ -> maybe [] (\ sa -> sa varCxt s) saMay
432+ SuccessCall _creq SuccessApiModel {shrinkAction= saMay} _ _ -> maybe [] (\ sa -> sa varCxt s) saMay
433+ ErrorCall {} -> error " TODO:"
434+ SomeExceptionCall {} -> error " TODO:"
390435
391436
392437data ErrorState =
@@ -414,29 +459,28 @@ data ResultError = MkResultError
414459 { err :: T. Text
415460 } deriving (Show )
416461
417- data ModifyClientCookies
462+ data ModifyClientCookies app
418463 = SetClientCookies [SetCookie ]
419- | ModifyClientCookies [( ByteString , SetCookieUpdate )]
464+ | ModifyClientCookies ( ClientCookies -> ClientCookies )
420465 | DeleteClientCookies [ByteString ]
421- | NoCookiesMod
422- deriving (Show )
423-
424- data SetCookieUpdate = SetCookieUpdate
425- { setCookieUpdateOpName :: Text
426- , setCookieUpdateOp :: SetCookie -> SetCookie
427- }
428-
429- instance Show SetCookieUpdate where
430- show SetCookieUpdate {setCookieUpdateOpName} = T. unpack setCookieUpdateOpName
466+ -- deriving (Show)
431467
432468
433469instance (Reifies s (WebApiGlobalStateModel apps )) => RunModel (ApiState s apps ) (WebApiSessions apps ) where
434470 type Error (ApiState s apps ) (WebApiSessions apps ) = ErrorState
435471 perform _ act lkp = case act of
436- MkWebApiAction (SuccessCall creq' model cookMod f) -> do
472+ MkWebApiAction (SuccessCall creq' _model cookModMay f) -> do
437473 case resolveRequest lkp creq' of
438474 Just creq -> testClients creq >>= \ case
439- Success code out headerOut cookieOut -> pure $ either (Left . ResultError ) Right $ f $ ApiSuccess {code, out, headerOut, cookieOut}
475+ Success code out headerOut cookieOut -> do
476+ let apiSucc = ApiSuccess {code, out, headerOut, cookieOut}
477+ case cookModMay of
478+ Nothing -> pure ()
479+ Just cookMod -> case cookMod apiSucc of
480+ modCk@ (SetClientCookies setcooks) -> mapM_ (setClientCookie modCk) setcooks
481+ modCk@ (ModifyClientCookies modcooks) -> modifyClientCookies modCk modcooks
482+ modCk@ (DeleteClientCookies delcooks) -> mapM_ (deleteClientCookie modCk) delcooks
483+ pure $ either (Left . ResultError ) Right $ f apiSucc
440484 Failure (Right oerr) -> pure $ Left UnExpectedApiCrash
441485 { status = H. status500 -- TODO: Fix this
442486 , headerOut = [] -- TODO: Fix this
@@ -474,7 +518,7 @@ instance (Reifies s (WebApiGlobalStateModel apps)) => RunModel (ApiState s apps)
474518 postcondition _ _act _lkp _a = pure True
475519 postconditionOnFailure _ _act _lkp _a = pure True
476520
477- monitoring (_s, s') act _lkp res =
521+ monitoring (_s, s') act _lkp _res =
478522 QC. counterexample (" show res" ++ " <- " ++ actionName act ++ " \n -- State: " ++ show s')
479523 . QC. tabulate " Registry size" [" Val1" , " Val2" ]
480524
@@ -488,14 +532,14 @@ instance (Reifies s (WebApiGlobalStateModel apps)) => DynLogicModel (ApiState s
488532successCall :: forall meth r app apps s . WebApiActionCxt apps meth app r =>
489533 ClientRequestVal meth (app :// r )
490534 -> Action (ApiState s apps ) (ApiOut meth (app :// r ))
491- successCall creq = mkWebApiAction $ SuccessCall creq defSuccessApiModel NoCookiesMod (Right . getSuccessOut)
535+ successCall creq = mkWebApiAction $ SuccessCall creq defSuccessApiModel Nothing (Right . getSuccessOut)
492536
493537successCallWith :: forall meth r app res apps s . (Typeable res , WebApiActionCxt apps meth app r ) =>
494538 ClientRequestVal meth (app :// r )
495- -> ModifyClientCookies
539+ -> Maybe ( ApiSuccess meth ( app :// r ) -> ModifyClientCookies app )
496540 -> (ApiSuccess meth (app :// r ) -> Either ResultError res )
497541 -> Action (ApiState s apps ) res
498- successCallWith creq cookMod f = mkWebApiAction (SuccessCall creq defSuccessApiModel cookMod f)
542+ successCallWith creq cookModMay f = mkWebApiAction (SuccessCall creq defSuccessApiModel cookModMay f)
499543
500544-- data ShowDict a where
501545-- ShowDict :: Show a => ShowDict a
0 commit comments