Write more core tests, log mock operations.
This commit is contained in:
parent
803aed691a
commit
1a28fc50dd
@ -189,13 +189,51 @@ main = hspec $ parallel $ do
|
|||||||
in cookieExpires st' session `shouldSatisfy` maybe False (>= distantFuture)
|
in cookieExpires st' session `shouldSatisfy` maybe False (>= distantFuture)
|
||||||
|
|
||||||
describe "saveSession" $ do
|
describe "saveSession" $ do
|
||||||
it "should have more tests" pending
|
-- We already test the other functions that saveSession
|
||||||
|
-- calls. A single unit test just to be sure everything is
|
||||||
|
-- connected should be enough.
|
||||||
|
it "works for a complex example" pending
|
||||||
|
|
||||||
describe "invalidateIfNeeded" $ do
|
describe "invalidateIfNeeded" $ do
|
||||||
it "should have more tests" pending
|
it "should have more tests" pending
|
||||||
|
|
||||||
describe "saveSessionOnDb" $ do
|
describe "saveSessionOnDb" $ do
|
||||||
it "should have more tests" pending
|
it "inserts new sessions when there wasn't an old one" $ do
|
||||||
|
sto <- emptyMockStorage
|
||||||
|
st <- createState sto
|
||||||
|
let d = DecomposedSession a DoNotForceInvalidate m
|
||||||
|
m = M.fromList [("a", "b"), ("c", "d")]
|
||||||
|
a = Just "auth"
|
||||||
|
session <- saveSessionOnDb st fakenow Nothing d
|
||||||
|
getMockOperations sto `shouldReturn` [InsertSession session]
|
||||||
|
sessionAuthId session `shouldBe` a
|
||||||
|
sessionData session `shouldBe` m
|
||||||
|
sessionCreatedAt session `shouldBe` fakenow
|
||||||
|
sessionAccessedAt session `shouldBe` fakenow
|
||||||
|
|
||||||
|
it "replaces sesssions when there was an old one" $ do
|
||||||
|
let oldSession = Session
|
||||||
|
{ sessionKey = S "123456789-123456789-1234"
|
||||||
|
, sessionAuthId = Just "auth"
|
||||||
|
, sessionData = M.fromList [("a", "b"), ("c", "d")]
|
||||||
|
, sessionCreatedAt = TI.addUTCTime (-10) fakenow
|
||||||
|
, sessionAccessedAt = TI.addUTCTime (-5) fakenow }
|
||||||
|
sto <- prepareMockStorage [oldSession]
|
||||||
|
st <- createState sto
|
||||||
|
let d = DecomposedSession Nothing DoNotForceInvalidate m
|
||||||
|
m = M.fromList [("a", "b"), ("x", "y")]
|
||||||
|
session <- saveSessionOnDb st fakenow (Just oldSession) d
|
||||||
|
getMockOperations sto `shouldReturn` [ReplaceSession session]
|
||||||
|
session `shouldBe` oldSession
|
||||||
|
{ sessionData = m
|
||||||
|
, sessionAuthId = Nothing
|
||||||
|
, sessionAccessedAt = fakenow }
|
||||||
|
|
||||||
|
it "does not save session if it's empty and there wasn't an old one" $
|
||||||
|
pendingWith "wishlist"
|
||||||
|
|
||||||
|
it "does not save session if only difference was accessedAt, and it was less than threshold" $
|
||||||
|
pendingWith "wishlist"
|
||||||
|
|
||||||
describe "decomposeSession" $ do
|
describe "decomposeSession" $ do
|
||||||
prop "it is sane when not finding auth key or force invalidate key" $
|
prop "it is sane when not finding auth key or force invalidate key" $
|
||||||
@ -277,26 +315,40 @@ instance E.Exception TNTExplosion where
|
|||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | A mock operation that was executed.
|
||||||
|
data MockOperation =
|
||||||
|
GetSession SessionId
|
||||||
|
| DeleteSession SessionId
|
||||||
|
| DeleteAllSessionsOfAuthId AuthId
|
||||||
|
| InsertSession Session
|
||||||
|
| ReplaceSession Session
|
||||||
|
deriving (Eq, Show, Typeable)
|
||||||
|
|
||||||
|
|
||||||
-- | A mock storage used just for testing.
|
-- | A mock storage used just for testing.
|
||||||
data MockStorage =
|
data MockStorage =
|
||||||
MockStorage
|
MockStorage
|
||||||
{ mockSessions :: I.IORef (M.Map SessionId Session)
|
{ mockSessions :: I.IORef (M.Map SessionId Session)
|
||||||
|
, mockOperations :: I.IORef [MockOperation]
|
||||||
}
|
}
|
||||||
deriving (Typeable)
|
deriving (Typeable)
|
||||||
|
|
||||||
instance Storage MockStorage where
|
instance Storage MockStorage where
|
||||||
type TransactionM MockStorage = IO
|
type TransactionM MockStorage = IO
|
||||||
runTransactionM _ = id
|
runTransactionM _ = id
|
||||||
getSession sto sid =
|
getSession sto sid = do
|
||||||
-- We need to use atomicModifyIORef instead of readIORef
|
-- We need to use atomicModifyIORef instead of readIORef
|
||||||
-- because latter may be reordered (cf. "Memory Model" on
|
-- because latter may be reordered (cf. "Memory Model" on
|
||||||
-- Data.IORef's documentation).
|
-- Data.IORef's documentation).
|
||||||
|
addMockOperation sto (GetSession sid)
|
||||||
M.lookup sid <$> I.atomicModifyIORef' (mockSessions sto) (\a -> (a, a))
|
M.lookup sid <$> I.atomicModifyIORef' (mockSessions sto) (\a -> (a, a))
|
||||||
deleteSession sto sid =
|
deleteSession sto sid = do
|
||||||
I.atomicModifyIORef' (mockSessions sto) ((, ()) . M.delete sid)
|
I.atomicModifyIORef' (mockSessions sto) ((, ()) . M.delete sid)
|
||||||
deleteAllSessionsOfAuthId sto authId =
|
addMockOperation sto (DeleteSession sid)
|
||||||
|
deleteAllSessionsOfAuthId sto authId = do
|
||||||
I.atomicModifyIORef' (mockSessions sto) ((, ()) . M.filter (\s -> sessionAuthId s /= Just authId))
|
I.atomicModifyIORef' (mockSessions sto) ((, ()) . M.filter (\s -> sessionAuthId s /= Just authId))
|
||||||
insertSession sto session =
|
addMockOperation sto (DeleteAllSessionsOfAuthId authId)
|
||||||
|
insertSession sto session = do
|
||||||
join $ I.atomicModifyIORef' (mockSessions sto) $ \oldMap ->
|
join $ I.atomicModifyIORef' (mockSessions sto) $ \oldMap ->
|
||||||
let (moldVal, newMap) =
|
let (moldVal, newMap) =
|
||||||
M.insertLookupWithKey (\_ v _ -> v) (sessionKey session) session oldMap
|
M.insertLookupWithKey (\_ v _ -> v) (sessionKey session) session oldMap
|
||||||
@ -304,7 +356,8 @@ instance Storage MockStorage where
|
|||||||
(newMap, return ())
|
(newMap, return ())
|
||||||
(\oldVal -> (oldMap, E.throwIO $ SessionAlreadyExists oldVal session))
|
(\oldVal -> (oldMap, E.throwIO $ SessionAlreadyExists oldVal session))
|
||||||
moldVal
|
moldVal
|
||||||
replaceSession sto session =
|
addMockOperation sto (InsertSession session)
|
||||||
|
replaceSession sto session = do
|
||||||
join $ I.atomicModifyIORef' (mockSessions sto) $ \oldMap ->
|
join $ I.atomicModifyIORef' (mockSessions sto) $ \oldMap ->
|
||||||
let (moldVal, newMap) =
|
let (moldVal, newMap) =
|
||||||
M.insertLookupWithKey (\_ v _ -> v) (sessionKey session) session oldMap
|
M.insertLookupWithKey (\_ v _ -> v) (sessionKey session) session oldMap
|
||||||
@ -312,6 +365,7 @@ instance Storage MockStorage where
|
|||||||
(oldMap, E.throwIO $ SessionDoesNotExist session)
|
(oldMap, E.throwIO $ SessionDoesNotExist session)
|
||||||
(const (newMap, return ()))
|
(const (newMap, return ()))
|
||||||
moldVal
|
moldVal
|
||||||
|
addMockOperation sto (ReplaceSession session)
|
||||||
|
|
||||||
|
|
||||||
-- | Creates empty mock storage.
|
-- | Creates empty mock storage.
|
||||||
@ -319,6 +373,7 @@ emptyMockStorage :: IO MockStorage
|
|||||||
emptyMockStorage =
|
emptyMockStorage =
|
||||||
MockStorage
|
MockStorage
|
||||||
<$> I.newIORef M.empty
|
<$> I.newIORef M.empty
|
||||||
|
<*> I.newIORef []
|
||||||
|
|
||||||
|
|
||||||
-- | Creates mock storage with the given sessions already existing.
|
-- | Creates mock storage with the given sessions already existing.
|
||||||
@ -327,3 +382,14 @@ prepareMockStorage sessions = do
|
|||||||
sto <- emptyMockStorage
|
sto <- emptyMockStorage
|
||||||
I.writeIORef (mockSessions sto) (M.fromList [(sessionKey s, s) | s <- sessions])
|
I.writeIORef (mockSessions sto) (M.fromList [(sessionKey s, s) | s <- sessions])
|
||||||
return sto
|
return sto
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get the list of mock operations that were made and clear
|
||||||
|
-- them. The operations are listed in chronological order.
|
||||||
|
getMockOperations :: MockStorage -> IO [MockOperation]
|
||||||
|
getMockOperations = flip I.atomicModifyIORef' ((,) []) . mockOperations
|
||||||
|
|
||||||
|
|
||||||
|
-- | Add a mock operations to the log.
|
||||||
|
addMockOperation :: MockStorage -> MockOperation -> IO ()
|
||||||
|
addMockOperation sto op = I.atomicModifyIORef' (mockOperations sto) $ \ops -> (op:ops, ())
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user