Skip to content

Commit

Permalink
Add several tests
Browse files Browse the repository at this point in the history
  • Loading branch information
imalsogreg committed Jun 25, 2014
1 parent 137d7bb commit 26818c9
Show file tree
Hide file tree
Showing 12 changed files with 337 additions and 10 deletions.
5 changes: 5 additions & 0 deletions devel.cfg
@@ -0,0 +1,5 @@
minPasswordLen = 20
rememberCookie = "myCookie"
rememberPeriod = 500000
lockout = [10,100000]
siteKey = "my_site_key.txt"
2 changes: 2 additions & 0 deletions runTestsAndCoverage.sh
Expand Up @@ -49,6 +49,8 @@ Snap.TestCommon
Snap.Snaplet.Test.App
Snap.Snaplet.Test.Tests
Snap.Snaplet.Auth.SpliceTests
Snap.Snaplet.Auth.Types.Tests
Snap.Snaplet.Config.App
Snap.Snaplet.Config.Tests
'

Expand Down
2 changes: 2 additions & 0 deletions snap.cabal
Expand Up @@ -233,6 +233,7 @@ Test-suite testsuite

build-depends:
aeson >= 0.6 && < 0.8,
async,
attoparsec >= 0.10 && < 0.13,
base >= 4.4 && < 5,
blaze-builder,
Expand All @@ -242,6 +243,7 @@ Test-suite testsuite
comonad >= 1.1 && < 4.3,
configurator >= 0.1 && < 0.3,
containers >= 0.3 && < 0.6,
deepseq,
directory >= 1.0 && < 1.3,
directory-tree >= 0.11 && < 0.13,
dlist >= 0.5 && < 0.8,
Expand Down
2 changes: 1 addition & 1 deletion src/Snap/Snaplet/Auth/Types.hs
Expand Up @@ -18,7 +18,7 @@ import qualified Data.HashMap.Strict as HM
import Data.Hashable (Hashable)
import Data.Time
import Data.Text (Text)
import Data.Text.Encoding
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Typeable
import Snap.Snaplet

Expand Down
26 changes: 20 additions & 6 deletions test/suite/Snap/Snaplet/Auth/App.hs
Expand Up @@ -9,6 +9,7 @@ module Snap.Snaplet.Auth.App
, heist
, authInit
, appInit
, appInit'
) where


Expand All @@ -28,6 +29,7 @@ import Snap.Snaplet.Auth.Backends.JsonFile
import Snap.Snaplet.Session.Backends.CookieSession
import Snap.Snaplet.Heist


------------------------------------------------------------------------------
data App = App
{ _sess :: Snaplet SessionManager
Expand All @@ -39,14 +41,17 @@ $(makeLenses ''App)
instance HasHeist App where
heistLens = subSnaplet heist


------------------------------------------------------------------------------
compiledSplices :: Splices (C.Splice (Handler App App))
compiledSplices = do
"userSplice" #! C.withSplices C.runChildren userCSplices $
lift $ maybe pass return =<< with auth currentUser


------------------------------------------------------------------------------
appInit :: SnapletInit App App
appInit = makeSnaplet "app" "Test application" Nothing $ do
appInit' :: Bool -> SnapletInit App App
appInit' useConfigFile = makeSnaplet "app" "Test application" Nothing $ do

h <- nestSnaplet "heist" heist $
heistInit'
Expand All @@ -57,15 +62,24 @@ appInit = makeSnaplet "app" "Test application" Nothing $ do
s <- nestSnaplet "sess" sess $
initCookieSessionManager "site_key.txt" "sess" (Just 3600)

a <- nestSnaplet "auth" auth authInit
authSettings <- if useConfigFile
then authSettingsFromConfig
else return defAuthSettings

a <- nestSnaplet "auth" auth $ authInit authSettings

addAuthSplices h auth

return $ App s a h


------------------------------------------------------------------------------
authInit :: SnapletInit App (AuthManager App)
authInit = initJsonFileAuthManager
defAuthSettings { asLockout = Just (3, 1) }
appInit :: SnapletInit App App
appInit = appInit' False


------------------------------------------------------------------------------
authInit :: AuthSettings -> SnapletInit App (AuthManager App)
authInit settings = initJsonFileAuthManager
settings { asLockout = Just (3, 1) }
sess "users.json"
34 changes: 34 additions & 0 deletions test/suite/Snap/Snaplet/Auth/Handlers/Tests.hs
Expand Up @@ -28,7 +28,9 @@ tests :: Test
tests = testGroup "Snap.Snaplet.Auth.Handlers"
[mutuallyExclusive $ testGroup "createUser tests"
[ testCreateUserGood
, testWithCfgFile
, testCreateUserTimely
, testCreateUserWithRole
, testCreateEmptyUser
, testCreateDupUser
, testUsernameExists
Expand Down Expand Up @@ -87,6 +89,17 @@ testCreateUserGood = testCase "createUser good params" assertGoodUser
failMsg = "createUser failed: Couldn't create a new user."


------------------------------------------------------------------------------
testWithCfgFile :: Test
testWithCfgFile = testCase "createUser with config file settings" assertCfg
where
assertCfg :: Assertion
assertCfg = withTemporaryFile "users.json" $ do
let hdl = with auth $ createUser "foo" "foo"
res <- runHandler Nothing (ST.get "" Map.empty) hdl (appInit' True)
either (assertFailure . show) ST.assertSuccess res


------------------------------------------------------------------------------
testCreateUserTimely :: Test
testCreateUserTimely = testCase "createUser good updatedAt" assertCreateTimely
Expand All @@ -106,6 +119,27 @@ testCreateUserTimely = testCase "createUser good updatedAt" assertCreateTimely
failMsg = "createUser: userUpdatedAt, userCreatetAt times not set"


------------------------------------------------------------------------------
testCreateUserWithRole :: Test
testCreateUserWithRole = testCase "createUser with role" assertUserRole
where
assertUserRole :: Assertion
assertUserRole = withTemporaryFile "users.json" $ do
let hdl = with auth $ runMaybeT $ do
u <- hushT $ EitherT $ createUser "foo" "foo"
_ <- hushT $ EitherT $
saveUser $ u {userRoles = [Role "admin",Role "user"]}
hushT $ EitherT $
loginByUsername "foo" (ClearText "foo") False
res <- evalHandler Nothing (ST.get "" Map.empty) hdl appInit
case res of
Left e -> assertFailure $ show e
Right Nothing -> assertFailure "Failed saved user lookup"
Right (Just usr) -> assertEqual "Roles don't match expectation"
[Role "admin",Role "user"]
(userRoles usr)


------------------------------------------------------------------------------
testCreateEmptyUser :: Test
testCreateEmptyUser = testCase "createUser empty username" assertEmptyUser
Expand Down
2 changes: 2 additions & 0 deletions test/suite/Snap/Snaplet/Auth/Tests.hs
Expand Up @@ -9,6 +9,7 @@ module Snap.Snaplet.Auth.Tests
------------------------------------------------------------------------------
import Test.Framework
import qualified Snap.Snaplet.Auth.Handlers.Tests
import qualified Snap.Snaplet.Auth.Types.Tests
import qualified Snap.Snaplet.Auth.SpliceTests


Expand All @@ -17,5 +18,6 @@ tests :: Test
tests = testGroup "Snap.Snaplet.Auth"
[ Snap.Snaplet.Auth.Handlers.Tests.tests
, Snap.Snaplet.Auth.SpliceTests.tests
, Snap.Snaplet.Auth.Types.Tests.tests
]

184 changes: 184 additions & 0 deletions test/suite/Snap/Snaplet/Auth/Types/Tests.hs
@@ -0,0 +1,184 @@
module Snap.Snaplet.Auth.Types.Tests (
tests
) where

------------------------------------------------------------------------------
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy.Char8 as BSL
import qualified Data.Map as Map
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Time
import Data.Time.Clock
import GHC.Read
import Test.HUnit hiding (Test)
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2
import Test.QuickCheck
import qualified Test.QuickCheck.Monadic as QCM
import Text.ParserCombinators.ReadPrec
------------------------------------------------------------------------------
import Snap.Core
import qualified Snap.Test as ST
import Snap.Snaplet.Test
import Heist
import Snap.Snaplet.Auth
import Snap.Snaplet.Heist
import Snap.TestCommon

------------------------------------------------------------------------------
tests :: Test
tests = testGroup "Auth type tests" [
testCase "Password serialization" dontSerializeClearText
, testCase "Fill in [] roles" deserializeDefaultRoles
, testCase "Fail deserialization" failDeserialize
, testProperty "AuthFailure show instances" authFailureShows
, testProperty "Encrypt agrees with password" encryptByteString
, testCase "Reject clear encrypted pw check" rejectCheckClearText
, testCase "Test Role Show instance" $ showTestCase (Role "a")
, testCase "Test Role Read instance" $ readTestCase (Role "a")
, testCase "Test Role Ord instance" $
ordTestCase (Role "a") (Role "b")
, testCase "Test PW Show instance" $
showTestCase (ClearText "pw")
, testCase "Test PW Read instance" $
readTestCase (ClearText "pw")
, testCase "Test PW Ord instance" $
ordTestCase (ClearText "a") (ClearText "b")
, testCase "Test AuthFailure Eq instance" $
eqTestCase BackendError DuplicateLogin --TODO better as property
, testCase "Test AuthFailure Show instance" $
showTestCase BackendError
-- , testCase "Test AuthFailure Read instance" $
-- readTestCase BackendError -- TODO/NOTE: show . read isn't id for
, testCase "Test AuthFailure Ord instance" $
ordTestCase BackendError DuplicateLogin
, testCase "Test UserId Show instance" $
showTestCase (UserId "1")
, testCase "Test UserId Read instance" $
readTestCase (UserId "2")
, testCase "Test AuthUser Show instance" $
showTestCase defAuthUser
, testCase "Test AuthUser Eq instance" $
eqTestCase defAuthUser defAuthUser
]


------------------------------------------------------------------------------
dontSerializeClearText :: Assertion
dontSerializeClearText = do
let s = encode (ClearText "passwordisnthamster")
r <- try $ s `deepseq` return s
case r of
Left e -> (e :: SomeException) `seq` return ()
Right j -> assertFailure $
"Failed to reject ClearText password serialization: "
++ show j


------------------------------------------------------------------------------
sampleUserJson :: T.Text -> T.Text -> T.Text
sampleUserJson reqPair optPair = T.intercalate "," [
"{\"uid\":\"1\""
, "\"login\":\"foo\""
, "\"email\":\"test@example.com\""
, "\"pw\":\"sha256|12|gz47sA0OvbVjos51OJRauQ==|Qe5aU2zAH0gIKHP68KrHJkvvwTvTAqA6UgA33BRpNEo=\""
, reqPair
, "\"suspended_at\":null"
, "\"remember_token\":\"81160620ef9b64865980c2ab760fcf7f14c06e057cbe1e723cba884a9be05547\""
, "\"login_count\":2"
, "\"failed_login_count\":1"
, "\"locked_until\":null"
, "\"current_login_at\":\"2014-06-24T14:43:51.241Z\""
, "\"last_login_at\":null"
, "\"current_ip\":\"127.0.0.1\""
, "\"last_ip\":null"
, "\"created_at\":\"2014-06-24T14:43:51.236Z\""
, "\"updated_at\":\"2014-06-24T14:43:51.242Z\""
, "\"reset_token\":null"
, "\"reset_requested_at\":null"
, optPair
, "\"meta\":{}}"
]


------------------------------------------------------------------------------
deserializeDefaultRoles :: Assertion
deserializeDefaultRoles =
either
(\e -> assertFailure $ "Failed user deserialization: " ++ e)
(\u -> assertEqual "Roles wasn't initialized to empty" [] (userRoles u))
(eitherDecode . BSL.fromStrict . encodeUtf8 $
sampleUserJson "\"activated_at\":null" "\"extra\":null")


------------------------------------------------------------------------------
failDeserialize :: Assertion
failDeserialize = do
case decode . BSL.fromStrict . encodeUtf8 $ t of
Nothing -> return ()
Just a -> assertFailure $
"Expected deserialization failure, got authUser: "
++ show (a :: AuthUser)

where
t = T.replace "login" "loogin" $
sampleUserJson "\"extra\":null" "\"extra2\":null"


------------------------------------------------------------------------------
authFailureShows :: AuthFailure -> Bool
authFailureShows ae = length (show ae) > 0


------------------------------------------------------------------------------
instance Arbitrary AuthFailure where
arbitrary = do
s <- (arbitrary `suchThat` (( > 0 ) . length))
tA <- arbitrary
tB <- arbitrary
let t = UTCTime
(ModifiedJulianDay tA)
(realToFrac (tB :: Double))
oneof (map return [AuthError s, BackendError, DuplicateLogin
,EncryptedPassword, IncorrectPassword, LockedOut t
,PasswordMissing, UsernameMissing, UserNotFound])


------------------------------------------------------------------------------
encryptByteString :: Property
encryptByteString = QCM.monadicIO testStringEq
where
clearPw = BS.pack `liftM` (arbitrary `suchThat` ((>0) . length))
testStringEq = QCM.forAllM clearPw $ \s -> do
ePW <- Encrypted `liftM` (QCM.run $ encrypt s)
ePW' <- QCM.run $ encryptPassword (ClearText s)
let cPW = ClearText s
{- QCM.assert $ (checkPassword cPW ePW
&& checkPassword cPW cPW
&& checkPassword ePW ePW') --TODO/NOTe: This fails.
Surpsising?
Encrypt twice and get two
different password hashes -}
QCM.assert $ (checkPassword cPW ePW
&& checkPassword cPW (ClearText s))


------------------------------------------------------------------------------
rejectCheckClearText :: Assertion
rejectCheckClearText = do
let b = checkPassword (Encrypted "") (ClearText "")
r <- try $ b `seq` return b
case r of
Left e -> (e :: SomeException) `seq` return ()
Right _ -> assertFailure
"checkPassword should not accept encripted-clear pair"

0 comments on commit 26818c9

Please sign in to comment.