Skip to content

Commit 6168506

Browse files
committed
Add ability to defined arbitraryAction using reification
1 parent 2f41cba commit 6168506

2 files changed

Lines changed: 74 additions & 70 deletions

File tree

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
module Test.WebApi.DynamicLogic
2-
( successCall
3-
, successCallWith
4-
, errorCall
5-
, someExceptionCall
6-
, propDL
2+
( propDL
73
, prop_api
4+
, runWebApiTest
85
, module Test.WebApi.StateModel
6+
, Reifies
97
) where
108

119
import Test.WebApi.StateModel
@@ -22,36 +20,17 @@ import Test.QuickCheck
2220
import Test.QuickCheck.Monadic
2321
import Test.QuickCheck.Monadic qualified as QC
2422
import Test.QuickCheck.Extras
23+
import Data.Reflection
2524

25+
propDL :: forall apps s. Reifies s (WebApiGlobalStateModel apps) => Proxy s -> (forall a. WebApiSessions apps a -> IO a) -> DL (ApiState s apps) () -> Property
26+
propDL _ webapiRunner d = forAllDL d (prop_api webapiRunner)
2627

27-
successCall :: forall meth r app apps. WebApiActionCxt apps meth app r =>
28-
ClientRequestVal meth (app :// r)
29-
-> DL (ApiState apps) (Var (ApiOut meth (app :// r)))
30-
successCall creq = action (mkWebApiAction (SuccessCall creq defSuccessApiModel NoCookiesMod (Right . getSuccessOut)))
31-
32-
successCallWith :: forall meth r app res apps. (Typeable res, WebApiActionCxt apps meth app r) =>
33-
ClientRequestVal meth (app :// r)
34-
-> ModifyClientCookies
35-
-> (ApiSuccess meth (app :// r) -> Either ResultError res)
36-
-> DL (ApiState apps) (Var res)
37-
successCallWith creq cookMod f = action (mkWebApiAction (SuccessCall creq defSuccessApiModel cookMod f))
38-
39-
errorCall :: forall meth r app apps.WebApiActionCxt apps meth app r =>
40-
ClientRequest meth (app :// r)
41-
-> DL (ApiState apps) (Val (ApiErr meth (app :// r)))
42-
errorCall creq = action (mkWebApiAction (ErrorCall creq)) >>= (pure . Var id)
43-
44-
someExceptionCall :: forall meth r app apps. WebApiActionCxt apps meth app r =>
45-
ClientRequest meth (app :// r)
46-
-> DL (ApiState apps) (Var SomeException)
47-
someExceptionCall creq = action (mkWebApiAction (SomeExceptionCall creq))
48-
49-
propDL :: (forall a. WebApiSessions apps a -> IO a) -> DL (ApiState apps) () -> Property
50-
propDL webapiRunner d = forAllDL d (prop_api webapiRunner)
51-
52-
prop_api :: forall apps. (forall a. WebApiSessions apps a -> IO a) -> Actions (ApiState apps) -> Property
28+
prop_api :: forall apps s. Reifies s (WebApiGlobalStateModel apps) => (forall a. WebApiSessions apps a -> IO a) -> Actions (ApiState s apps) -> Property
5329
prop_api webapiRunner s =
5430
monadic (ioProperty . webapiRunner) $ do
5531
monitor $ counterexample "\nExecution\n"
5632
_ <- runActions s
5733
QC.assert True
34+
35+
runWebApiTest :: WebApiGlobalStateModel apps -> (forall (s :: Type). Reifies s (WebApiGlobalStateModel apps) => Proxy s -> r) -> r
36+
runWebApiTest gstate runner = reify gstate (\ps -> runner ps)

webapi-test/quickcheck-dynamic/Test/WebApi/StateModel.hs

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ module Test.WebApi.StateModel
1313
, DSum (..)
1414
, Val (..)
1515
, ClientRequestVal (..)
16+
, WebApiGlobalStateModel (..)
17+
, successCall
18+
, successCallWith
1619
, mkWebApiAction
1720
, getOpIdFromRequest
1821
, getSuccessOut
@@ -52,7 +55,7 @@ import System.IO.Unsafe
5255
import qualified Unsafe.Coerce as Unsafe
5356
import qualified GHC.Base as Unsafe (Any)
5457
import qualified Record
55-
-- import Data.Reflection
58+
import Data.Reflection
5659

5760
type WebApiActionCxt (apps :: [Type]) (meth :: Type) (app :: Type) (r :: k) =
5861
( ToParam 'PathParam (PathParam meth (app://r))
@@ -186,28 +189,28 @@ resolveRequest lkp ClientRequestVal {query, form, header, path, file, body} =
186189
body <- Just $ resolveVal lkp body
187190
Just $ ClientRequest {query, form, header, path, file, body}
188191

189-
data WebApiAction (apps :: [Type]) (a :: Type) where
192+
data WebApiAction s (apps :: [Type]) (a :: Type) where
190193
SuccessCall :: WebApiActionCxt apps meth app r
191194
=> ClientRequestVal meth (app :// r)
192-
-> SuccessApiModel apps meth (app :// r) res
195+
-> SuccessApiModel s apps meth (app :// r) res
193196
-> ModifyClientCookies
194197
-> (ApiSuccess meth (app :// r) -> Either ResultError res)
195-
-> WebApiAction apps res
198+
-> WebApiAction s apps res
196199
ErrorCall :: WebApiActionCxt apps meth app r
197200
=> ClientRequest meth (app :// r)
198-
-> WebApiAction apps (ApiErr meth (app :// r))
201+
-> WebApiAction s apps (ApiErr meth (app :// r))
199202
SomeExceptionCall :: WebApiActionCxt apps meth app r
200203
=> ClientRequest meth (app :// r)
201-
-> WebApiAction apps (SomeException)
204+
-> WebApiAction s apps (SomeException)
202205

203-
instance Show (WebApiAction apps a) where
206+
instance Show (WebApiAction s apps a) where
204207
show = \case
205208
SuccessCall creq _ _ _ -> show . getOpIdFromRequest $ creq
206209
ErrorCall creq -> show . unsafePerformIO . toWaiRequest . fromClientRequest $ creq
207210
SomeExceptionCall creq -> show . unsafePerformIO . toWaiRequest . fromClientRequest $ creq
208211

209212
-- TODO: Revisit
210-
instance Eq (WebApiAction apps a) where
213+
instance Eq (WebApiAction s apps a) where
211214
(==) (SuccessCall creq1 _ _ _) = \case
212215
SuccessCall creq2 _ _ _ -> (getOpIdFromRequest $ creq1) == (getOpIdFromRequest $ creq2)
213216
_ -> False
@@ -218,7 +221,7 @@ instance Eq (WebApiAction apps a) where
218221
SomeExceptionCall creq2 -> (show . unsafePerformIO . toWaiRequest . fromClientRequest $ creq1) == (show . unsafePerformIO . toWaiRequest . fromClientRequest $ creq2)
219222
_ -> False
220223

221-
instance HasVariables (WebApiAction apps a) where
224+
instance HasVariables (WebApiAction s apps a) where
222225
getAllVariables = \case
223226
SuccessCall creq _ _ _ -> getAllVariables creq
224227

@@ -275,19 +278,19 @@ instance Monoid NamedEntityKeyed where
275278
newtype Trail = Trail [Text]
276279
deriving newtype (Show, Eq, Ord, Read)
277280

278-
data ApiState (apps :: [Type]) = ApiState
281+
data ApiState (s :: Type) (apps :: [Type]) = ApiState
279282
{ apiState :: M.Map TypeRep Unsafe.Any
280283
, namedEntityTyped :: NamedEntityTyped
281284
, namedEntityKeyed :: NamedEntityKeyed
282285
}
283286

284-
instance Show (ApiState apps) where
287+
instance Show (ApiState s apps) where
285288
show (ApiState {apiState}) = "undefined" -- showTaggedPrec p $DMap.dmap
286289

287-
instance Eq (ApiState apps) where
290+
instance Eq (ApiState s apps) where
288291
s1 == s2 = undefined
289292

290-
modifyApiState :: forall app apps stTag. (Typeable app, AppIsElem app apps) => DSum (stTag apps app) Proxy -> (DSum (stTag apps app) Identity -> DSum (stTag apps app) Identity) -> ApiState apps -> ApiState apps
293+
modifyApiState :: 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
291294
modifyApiState ctor@(tag :=> _) f (ApiState {apiState = stMap, namedEntityTyped}) = case M.lookup (typeRep (getAppProxy' ctor)) stMap of
292295
Nothing -> undefined
293296
Just anyv -> case f (tag :=> (Identity $ castToTagVal tag anyv)) of
@@ -302,7 +305,7 @@ modifyApiState ctor@(tag :=> _) f (ApiState {apiState = stMap, namedEntityTyped}
302305
class HasApiState (apps1 :: [Type]) stTag (apps :: [Type]) where
303306
apiStateUniv :: Proxy apps1 -> (forall app. Typeable app => DSum (stTag apps app) Proxy -> r) -> [r]
304307

305-
initApiState :: forall apps stTag. HasApiState apps stTag apps => (forall app. Typeable app => DSum (stTag apps app) Proxy -> DSum (stTag apps app) Identity) -> ApiState apps
308+
initApiState :: 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
306309
initApiState f = ApiState { apiState = M.fromList $ apiStateUniv (Proxy @apps) $ \ctor -> case f ctor of
307310
tag :=> (Identity v) -> (typeRep (getAppProxy' ctor), Unsafe.unsafeCoerce v :: Unsafe.Any)
308311
}
@@ -312,20 +315,20 @@ getAppProxy' :: forall stTag apps app f. Typeable app => DSum (stTag apps app) f
312315
getAppProxy' _ = Proxy
313316

314317

315-
instance HasVariables (ApiState apps) where
318+
instance HasVariables (ApiState s apps) where
316319
getAllVariables = mempty
317320

318-
data SuccessApiModel apps meth r a = SuccessApiModel
319-
{ nextState :: Maybe (Var a -> ApiState apps -> ApiState apps)
320-
, failureNextState :: Maybe (ApiState apps -> ApiState apps)
321-
, precondition :: Maybe (ApiState apps -> Bool)
322-
, validFailingAction :: Maybe (ApiState apps -> Bool)
323-
, shrinkAction :: Maybe (VarContext -> ApiState apps -> [Any (Action (ApiState apps))])
324-
, postCondition :: (ApiState apps, ApiState apps) -> LookUp -> a -> Bool
325-
, postconditionOnFailure :: (ApiState apps, ApiState apps) -> LookUp -> Either ErrorState a -> Bool
321+
data SuccessApiModel s apps meth r a = SuccessApiModel
322+
{ nextState :: Maybe (Var a -> ApiState s apps -> ApiState s apps)
323+
, failureNextState :: Maybe (ApiState s apps -> ApiState s apps)
324+
, precondition :: Maybe (ApiState s apps -> Bool)
325+
, validFailingAction :: Maybe (ApiState s apps -> Bool)
326+
, shrinkAction :: Maybe (VarContext -> ApiState s apps -> [Any (Action (ApiState s apps))])
327+
, postCondition :: (ApiState s apps, ApiState s apps) -> LookUp -> a -> Bool
328+
, postconditionOnFailure :: (ApiState s apps, ApiState s apps) -> LookUp -> Either ErrorState a -> Bool
326329
}
327330

328-
defSuccessApiModel :: SuccessApiModel apps meth r a
331+
defSuccessApiModel :: SuccessApiModel s apps meth r a
329332
defSuccessApiModel = SuccessApiModel
330333
{ nextState = Nothing
331334
, failureNextState = Nothing
@@ -334,31 +337,41 @@ defSuccessApiModel = SuccessApiModel
334337
, shrinkAction = Nothing
335338
}
336339

337-
data FailureApiModel apps meth r a = FailureApiModel
338-
{ failureNextState :: Maybe (ApiState apps -> ApiState apps)
339-
, precondition :: Maybe (ApiState apps -> Bool)
340-
, validFailingAction :: Maybe (ApiState apps -> Bool)
341-
, shrinkAction :: Maybe (VarContext -> ApiState apps -> [Any (Action (ApiState apps))])
342-
, postconditionOnFailure :: (ApiState apps, ApiState apps) -> LookUp -> Either ErrorState a -> Bool
340+
data FailureApiModel s apps meth r a = FailureApiModel
341+
{ failureNextState :: Maybe (ApiState s apps -> ApiState s apps)
342+
, precondition :: Maybe (ApiState s apps -> Bool)
343+
, validFailingAction :: Maybe (ApiState s apps -> Bool)
344+
, shrinkAction :: Maybe (VarContext -> ApiState s apps -> [Any (Action (ApiState s apps))])
345+
, postconditionOnFailure :: (ApiState s apps, ApiState s apps) -> LookUp -> Either ErrorState a -> Bool
343346
}
344347

345-
mkWebApiAction :: WebApiAction apps a -> Action (ApiState apps) a
348+
mkWebApiAction :: WebApiAction s apps a -> Action (ApiState s apps) a
346349
mkWebApiAction = coerce
347350

348351
newtype ApiInitState apps = ApiInitState (M.Map TypeRep Unsafe.Any)
349352

350-
instance StateModel (ApiState apps) where
351-
newtype Action (ApiState apps) a = MkWebApiAction (WebApiAction apps a)
353+
data WebApiGlobalStateModel apps = WebApiGlobalStateModel
354+
{ appAribitaryAction :: forall (s :: Type). VarContext -> ApiState s apps -> Any (Action (ApiState s apps))
355+
, appInitState :: ApiInitState apps
356+
, namedEntityTyped :: NamedEntityTyped
357+
, namedEntityKeyed :: NamedEntityKeyed
358+
}
359+
360+
instance (Reifies s (WebApiGlobalStateModel apps)) => StateModel (ApiState s apps) where
361+
newtype Action (ApiState s apps) a = MkWebApiAction (WebApiAction s apps a)
352362
deriving newtype (Show, Eq, HasVariables)
353363

354364
actionName = \case
355365
MkWebApiAction (SuccessCall creq _ _ _) -> getOpIdFromRequest creq
356366
MkWebApiAction (ErrorCall creq) -> getOpIdFromRequest creq
357367
MkWebApiAction (SomeExceptionCall creq) -> getOpIdFromRequest creq
358368

359-
arbitraryAction _varCxt _s = pure undefined
369+
arbitraryAction varCxt s = case reflect (Proxy @s) of
370+
WebApiGlobalStateModel {appAribitaryAction} -> pure $ appAribitaryAction @s varCxt s
360371

361-
initialState = ApiState {apiState = mempty, namedEntityTyped = mempty}
372+
initialState =
373+
let WebApiGlobalStateModel {appInitState, namedEntityTyped, namedEntityKeyed} = reflect (Proxy @s)
374+
in ApiState {apiState = coerce appInitState, namedEntityTyped, namedEntityKeyed}
362375

363376
nextState s (MkWebApiAction act) var = case act of
364377
SuccessCall creq SuccessApiModel {nextState=nsMay} _ _ -> maybe s (\ns -> ns var s) nsMay
@@ -417,8 +430,8 @@ instance Show SetCookieUpdate where
417430
show SetCookieUpdate {setCookieUpdateOpName} = T.unpack setCookieUpdateOpName
418431

419432

420-
instance RunModel (ApiState apps) (WebApiSessions apps) where
421-
type Error (ApiState apps) (WebApiSessions apps) = ErrorState
433+
instance (Reifies s (WebApiGlobalStateModel apps)) => RunModel (ApiState s apps) (WebApiSessions apps) where
434+
type Error (ApiState s apps) (WebApiSessions apps) = ErrorState
422435
perform _ act lkp = case act of
423436
MkWebApiAction (SuccessCall creq' model cookMod f) -> do
424437
case resolveRequest lkp creq' of
@@ -469,13 +482,25 @@ instance RunModel (ApiState apps) (WebApiSessions apps) where
469482
QC.counterexample (show err ++ " <- " ++ actionName act ++ "\n -- State: " ++ show s')
470483
. QC.tabulate "Registry size" ["Val1", "Val2"]
471484

472-
instance DynLogicModel (ApiState apps) where
485+
instance (Reifies s (WebApiGlobalStateModel apps)) => DynLogicModel (ApiState s apps) where
473486
restricted _ = False
474487

488+
successCall :: forall meth r app apps s. WebApiActionCxt apps meth app r =>
489+
ClientRequestVal meth (app :// r)
490+
-> Action (ApiState s apps) (ApiOut meth (app :// r))
491+
successCall creq = mkWebApiAction $ SuccessCall creq defSuccessApiModel NoCookiesMod (Right . getSuccessOut)
492+
493+
successCallWith :: forall meth r app res apps s. (Typeable res, WebApiActionCxt apps meth app r) =>
494+
ClientRequestVal meth (app :// r)
495+
-> ModifyClientCookies
496+
-> (ApiSuccess meth (app :// r) -> Either ResultError res)
497+
-> Action (ApiState s apps) res
498+
successCallWith creq cookMod f = mkWebApiAction (SuccessCall creq defSuccessApiModel cookMod f)
499+
475500
-- data ShowDict a where
476501
-- ShowDict :: Show a => ShowDict a
477502

478-
-- showDictAction :: forall apps a. Action (ApiState apps) a -> ShowDict a
503+
-- showDictAction :: forall apps a. Action (ApiState s apps) a -> ShowDict a
479504
-- showDictAction = \case
480505
-- MkWebApiAction (SuccessCall {}) -> ShowDict
481506

0 commit comments

Comments
 (0)