Skip to content

Commit 20deaf2

Browse files
committed
core: Add getClientCookies, modifyClientCookies, setClientCookie, deleteClientCookie
qc-dynamic Fix shrinkVal, Handle Pair ctor in applicative instance and fix all statemodel warnings
1 parent 4835282 commit 20deaf2

File tree

5 files changed

+122
-68
lines changed

5 files changed

+122
-68
lines changed

cabal.project

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ packages:
33
webapi-contract
44
webapi
55
webapi-client-reflex-dom
6-
webapi-swagger
6+
-- webapi-swagger
77
webapi-docs
88
webapi-xml
9-
webapi-openapi
9+
-- webapi-openapi
1010
webapi-reflex-dom
1111
webapi-test
12+
-- ../rec/rec
1213

1314
source-repository-package
1415
type: git
1516
location: https://github.com/byteally/rec
16-
tag: 13b29ebfcf6f2a2230734ee6eee2f653bcf75594
17+
tag: 6deffbca324faca6c2c6ded28c38fea8f5edd716
1718
subdir: rec
1819

1920
allow-newer: typerep-map:base, typerep-map:ghc-prim

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Test.WebApi.DynamicLogic
66
, apiForAllVar
77
, getCtxAtTypeDL
88
, arbitraryVal
9+
, shrinkVal
910
, module Test.WebApi.StateModel
1011
, Reifies
1112
) where
@@ -14,18 +15,12 @@ import Test.WebApi.StateModel
1415
import Test.WebApi
1516
import Test.QuickCheck.StateModel
1617
import Test.QuickCheck.DynamicLogic
17-
import WebApi.Contract
18-
import WebApi.Param
19-
import WebApi.ContentTypes
20-
import Control.Exception (SomeException)
2118
import Data.Kind
2219
import Data.Typeable
2320
import Test.QuickCheck
2421
import Test.QuickCheck.Monadic
2522
import Test.QuickCheck.Monadic qualified as QC
26-
import Test.QuickCheck.Extras
2723
import Data.Reflection
28-
import Test.QuickCheck.StateModel.Variables
2924
import qualified Record
3025

3126
propDL :: forall apps s. Reifies s (WebApiGlobalStateModel apps) => Proxy s -> (forall a. WebApiSessions apps a -> IO a) -> DL (ApiState s apps) () -> Property
@@ -53,9 +48,10 @@ getCtxAtTypeDL = (fmap (Var id) . ctxAtType @a) <$> getVarContextDL
5348
arbitraryVal :: Typeable a => VarContext -> Gen (Val a)
5449
arbitraryVal = fmap (Var id) . arbitraryVar
5550

56-
shrinkVal :: Typeable a => VarContext -> Val a -> [Val a]
51+
shrinkVal :: forall a. Typeable a => VarContext -> Val a -> [Val a]
5752
shrinkVal vctx = \case
5853
v@Const {} -> [v]
5954
Var f v -> fmap (Var f) $ shrinkVar vctx v
6055
v@Opt {} -> [v]
61-
-- HKVal f hk -> concat $ Record.hkToListWith (shrinkVal' f vctx) hk
56+
HKVal f hk -> fmap (HKVal f) $ Record.hoistWithKeyHKA (shrinkVal vctx) hk
57+
Pair f (v1, v2) -> fmap (Pair f) $ (,) <$> (shrinkVal vctx v1) <*> (shrinkVal vctx v2)

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

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ module Test.WebApi.StateModel
2323
, getSuccessHeaders
2424
, getSuccessCookies
2525
, defSuccessApiModel
26+
, defFailureApiModel
2627
, initApiState
2728
, modifyApiState
2829
) where
2930

3031
import Test.WebApi
3132
import Test.QuickCheck.StateModel
3233
import Test.QuickCheck.DynamicLogic (DynLogicModel (..))
34+
-- import Test.QuickCheck.StateModel.Variables (Any (..))
3335
import qualified Test.QuickCheck as QC
3436
import WebApi.Contract
3537
import WebApi.Param
@@ -48,9 +50,7 @@ import Data.Functor.Identity
4850
import qualified Data.Text as T
4951
import qualified Data.Map.Strict as M
5052
import qualified Data.Set as Set
51-
import Data.Dependent.Map (DMap)
5253
import Data.Dependent.Sum (DSum (..))
53-
import qualified Data.Dependent.Map as DMap
5454
import System.IO.Unsafe
5555
import qualified Unsafe.Coerce as Unsafe
5656
import 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

116116
deriving 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

141147
resolveVal :: LookUp -> Val a -> a
142148
resolveVal 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

148155
instance 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

155163
data ClientRequestVal meth r = ClientRequestVal
@@ -181,19 +189,19 @@ resolveRequest ::
181189
-> Maybe (ClientRequest meth r)
182190
resolveRequest 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

192200
data 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
224232
instance 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

228238
newtype 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+
237256
instance 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

252271
newtype NamedEntityTyped = NamedEntityTyped (NamedEntity TypeRep)
253-
deriving newtype (Semigroup, Monoid)
272+
deriving newtype (Semigroup, Monoid, Show, Eq)
254273

255274
newtype 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

264283
instance 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

287306
instance 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

290310
instance 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

293314
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
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

308330
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
309331
initApiState 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

340366
data 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+
348383
mkWebApiAction :: WebApiAction s apps a -> Action (ApiState s apps) a
349384
mkWebApiAction = 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

392437
data 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

433469
instance (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
488532
successCall :: 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

493537
successCallWith :: 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

Comments
 (0)