[tests] more refactoring of tests with alice and bob

parent cc5d7465
...@@ -85,6 +85,7 @@ library ...@@ -85,6 +85,7 @@ library
Gargantext.API.Prelude Gargantext.API.Prelude
Gargantext.API.Routes Gargantext.API.Routes
Gargantext.Core Gargantext.Core
Gargantext.Core.Mail
Gargantext.Core.Mail.Types Gargantext.Core.Mail.Types
Gargantext.Core.Methods.Similarities Gargantext.Core.Methods.Similarities
Gargantext.Core.NLP Gargantext.Core.NLP
...@@ -228,7 +229,6 @@ library ...@@ -228,7 +229,6 @@ library
Gargantext.Core.Ext.IMTUser Gargantext.Core.Ext.IMTUser
Gargantext.Core.Flow.Ngrams Gargantext.Core.Flow.Ngrams
Gargantext.Core.Flow.Types Gargantext.Core.Flow.Types
Gargantext.Core.Mail
Gargantext.Core.Methods.Graph.BAC.Proxemy Gargantext.Core.Methods.Graph.BAC.Proxemy
Gargantext.Core.Methods.Graph.MaxClique Gargantext.Core.Methods.Graph.MaxClique
Gargantext.Core.Methods.Matrix.Accelerate.Utils Gargantext.Core.Methods.Matrix.Accelerate.Utils
...@@ -1021,6 +1021,7 @@ test-suite garg-test-tasty ...@@ -1021,6 +1021,7 @@ test-suite garg-test-tasty
, hspec ^>= 2.7.10 , hspec ^>= 2.7.10
, hspec-core , hspec-core
, hspec-expectations >= 0.8 && < 0.9 , hspec-expectations >= 0.8 && < 0.9
, hspec-expectations-json ^>= 1.0.2.1
, hspec-wai , hspec-wai
, hspec-wai-json , hspec-wai-json
, http-api-data , http-api-data
...@@ -1060,6 +1061,7 @@ test-suite garg-test-tasty ...@@ -1060,6 +1061,7 @@ test-suite garg-test-tasty
, tmp-postgres >= 1.34.1 && < 1.35 , tmp-postgres >= 1.34.1 && < 1.35
, unordered-containers ^>= 0.2.16.0 , unordered-containers ^>= 0.2.16.0
, validity ^>= 0.11.0.1 , validity ^>= 0.11.0.1
, vector ^>= 0.13.1
, wai , wai
, wai-extra , wai-extra
, warp , warp
...@@ -1132,6 +1134,7 @@ test-suite garg-test-hspec ...@@ -1132,6 +1134,7 @@ test-suite garg-test-hspec
, hspec ^>= 2.7.10 , hspec ^>= 2.7.10
, hspec-core , hspec-core
, hspec-expectations >= 0.8 && < 0.9 , hspec-expectations >= 0.8 && < 0.9
, hspec-expectations-json ^>= 1.0.2.1
, hspec-wai , hspec-wai
, hspec-wai-json , hspec-wai-json
, http-api-data , http-api-data
...@@ -1170,6 +1173,7 @@ test-suite garg-test-hspec ...@@ -1170,6 +1173,7 @@ test-suite garg-test-hspec
, tmp-postgres >= 1.34.1 && < 1.35 , tmp-postgres >= 1.34.1 && < 1.35
, unordered-containers ^>= 0.2.16.0 , unordered-containers ^>= 0.2.16.0
, validity ^>= 0.11.0.1 , validity ^>= 0.11.0.1
, vector ^>= 0.13.1
, wai , wai
, wai-extra , wai-extra
, warp , warp
......
...@@ -15,14 +15,13 @@ import Gargantext.API.Admin.Auth.Types ...@@ -15,14 +15,13 @@ import Gargantext.API.Admin.Auth.Types
import Gargantext.API.Routes import Gargantext.API.Routes
import Gargantext.Core.Types import Gargantext.Core.Types
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Action.User.New
import Gargantext.Prelude import Gargantext.Prelude
import Network.HTTP.Client hiding (Proxy) import Network.HTTP.Client hiding (Proxy)
import Prelude qualified import Prelude qualified
import Servant.Auth.Client () import Servant.Auth.Client ()
import Servant.Client import Servant.Client
import Test.API.Setup (withTestDBAndPort) import Test.API.Setup (withTestDBAndPort)
import Test.Database.Types import Test.Database.Setup (getUserOrFail)
import Test.Hspec import Test.Hspec
auth_api :: AuthRequest -> ClientM AuthResponse auth_api :: AuthRequest -> ClientM AuthResponse
...@@ -50,18 +49,19 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -50,18 +49,19 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
describe "POST /api/v1.0/auth" $ do describe "POST /api/v1.0/auth" $ do
it "requires no auth and authenticates the user 'alice'" $ \((testEnv, port), _) -> do it "requires no auth and authenticates the user 'alice'" $ \((testEnv, port), _) -> do
(_aliceEmail, alicePassword, aliceId, aliceNodeId) <- getUserOrFail testEnv "alice"
-- Let's create the Alice user. -- Let's create the Alice user.
void $ flip runReaderT testEnv $ runTestMonad $ do -- void $ flip runReaderT testEnv $ runTestMonad $ do
void $ new_user $ mkNewUser "alice@gargan.text" (GargPassword "alice") -- void $ new_user $ mkNewUser "alice@gargan.text" (GargPassword "alice")
let authPayload = AuthRequest "alice" (GargPassword "alice") let authPayload = AuthRequest "alice" alicePassword
result0 <- runClientM (auth_api authPayload) (clientEnv port) result0 <- runClientM (auth_api authPayload) (clientEnv port)
let result = over (_Right . authRes_token) (const cannedToken) result0 let result = over (_Right . authRes_token) (const cannedToken) result0
let expected = AuthResponse { let expected = AuthResponse {
_authRes_token = cannedToken _authRes_token = cannedToken
, _authRes_tree_id = fromMaybe (UnsafeMkNodeId 1) $ listToMaybe $ result0 ^.. _Right . authRes_tree_id , _authRes_tree_id = fromMaybe (UnsafeMkNodeId aliceNodeId) $ listToMaybe $ result0 ^.. _Right . authRes_tree_id
, _authRes_user_id = fromMaybe (UnsafeMkUserId 1) $ listToMaybe $ result0 ^.. _Right . authRes_user_id , _authRes_user_id = fromMaybe aliceId $ listToMaybe $ result0 ^.. _Right . authRes_user_id
} }
result `shouldBe` (Right expected) result `shouldBe` (Right expected)
......
...@@ -11,7 +11,7 @@ import Servant ...@@ -11,7 +11,7 @@ import Servant
import Servant.Auth.Client () import Servant.Auth.Client ()
import Servant.Client import Servant.Client
import Test.API.Private (protected, withValidLogin, protectedNewError) import Test.API.Private (protected, withValidLogin, protectedNewError)
import Test.API.Setup (withTestDBAndPort, mkUrl, createAliceAndBob) import Test.API.Setup (withTestDBAndPort, mkUrl)
import Test.Database.Setup (MasterUserEnv(..), getMasterUserEnvOrFail) import Test.Database.Setup (MasterUserEnv(..), getMasterUserEnvOrFail)
import Test.Hspec import Test.Hspec
import Test.Hspec.Wai.Internal (withApplication) import Test.Hspec.Wai.Internal (withApplication)
...@@ -22,13 +22,11 @@ tests :: Spec ...@@ -22,13 +22,11 @@ tests :: Spec
tests = sequential $ aroundAll withTestDBAndPort $ do tests = sequential $ aroundAll withTestDBAndPort $ do
describe "Errors API" $ do describe "Errors API" $ do
describe "Prelude" $ do describe "Prelude" $ do
it "setup DB users" $ \((testEnv, port), _) -> do it "setup DB users" $ \((_testEnv, port), _) -> do
baseUrl <- parseBaseUrl "http://localhost" baseUrl <- parseBaseUrl "http://localhost"
manager <- newManager defaultManagerSettings manager <- newManager defaultManagerSettings
let clientEnv prt = mkClientEnv manager (baseUrl { baseUrlPort = prt }) let clientEnv prt = mkClientEnv manager (baseUrl { baseUrlPort = prt })
void $ createAliceAndBob testEnv
let ( roots_api :<|> _nodes_api let ( roots_api :<|> _nodes_api
) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token "bogus") ) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token "bogus")
let ( admin_user_api_get :<|> _) = roots_api let ( admin_user_api_get :<|> _) = roots_api
...@@ -63,3 +61,5 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -63,3 +61,5 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
->liftIO $ do ->liftIO $ do
statusCode `shouldBe` 404 statusCode `shouldBe` 404
simpleBody `shouldBe` [r|{"data":{"node_id":99},"diagnostic":"FE_node_lookup_failed_not_found {nenf_node_id = nodeId-99}","type":"EC_404__node_lookup_failed_not_found"}|] simpleBody `shouldBe` [r|{"data":{"node_id":99},"diagnostic":"FE_node_lookup_failed_not_found {nenf_node_id = nodeId-99}","type":"EC_404__node_lookup_failed_not_found"}|]
...@@ -7,12 +7,16 @@ module Test.API.GraphQL ( ...@@ -7,12 +7,16 @@ module Test.API.GraphQL (
tests tests
) where ) where
import Data.Aeson ((.=))
import Data.Aeson qualified as JSON
import Data.Vector qualified as V
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Admin.Types.Node (UserId(..))
import Gargantext.Prelude import Gargantext.Prelude
import Servant.Auth.Client () import Servant.Auth.Client ()
import Test.API.Private (withValidLogin, protected, protectedNewError) import Test.API.Private (withValidLogin, protected, protectedNewError)
import Test.API.Setup (withTestDBAndPort, createAliceAndBob) import Test.API.Setup (withTestDBAndPort)
import Test.Database.Setup (MasterUserEnv(..), getMasterUserEnvOrFail) import Test.Database.Setup (MasterUserEnv(..), getMasterUserEnvOrFail, getUserOrFail)
import Test.Hspec import Test.Hspec
import Test.Hspec.Wai.Internal (withApplication) import Test.Hspec.Wai.Internal (withApplication)
import Test.Hspec.Wai.JSON (json) import Test.Hspec.Wai.JSON (json)
...@@ -26,13 +30,18 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -26,13 +30,18 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
describe "get_user_infos" $ do describe "get_user_infos" $ do
it "allows 'alice' to see her own info" $ \((testEnv, port), app) -> do it "allows 'alice' to see her own info" $ \((testEnv, port), app) -> do
void $ createAliceAndBob testEnv (aliceEmail, alicePassword, aliceId, _aliceNodeId) <- getUserOrFail testEnv "alice"
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
let query = [r| { "query": "{ user_infos(user_id: 2) { ui_id, ui_email } }" } |] let query' = "{ user_infos(user_id:" <> show (_UserId aliceId) <> ") { ui_id, ui_email } }" :: Text
let expected = [json| {"data":{"user_infos":[{"ui_id":2,"ui_email":"alice@gargan.text"}]}} |] let query = JSON.encode $ JSON.object [ "query" .= query' ]
protected token "POST" "/gql" query `shouldRespondWithFragment` expected liftIO $ printDebug "[get_user_infos] query" query
let userInfo = JSON.object [ "ui_id" .= aliceId
, "ui_email" .= aliceEmail ]
let expected =
JSON.object [ "data" .= JSON.object [ "user_infos" .= JSON.Array (V.singleton userInfo) ] ]
protected token "POST" "/gql" query `matchSimpleJSON` expected
describe "check error format" $ do describe "check error format" $ do
...@@ -41,18 +50,28 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -41,18 +50,28 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
mue <- getMasterUserEnvOrFail testEnv mue <- getMasterUserEnvOrFail testEnv
withValidLogin port (userName mue) (GargPassword $ secretKey mue) $ \token -> do withValidLogin port (userName mue) (GargPassword $ secretKey mue) $ \token -> do
let query = [r| { "query": "{ languages(id:5) { lt_lang } }" } |] let query' = "{ languages(id:5) { lt_lang } }" :: Text
let expected = [json| {"errors": [{"locations":[{"column":13,"line":1}],"message":"Unknown Argument \"id\" on Field \"languages\"."}] } |] let query = JSON.encode $ JSON.object [ "query" .= query' ]
protectedNewError token "POST" "/gql" query `shouldRespondWithFragment` expected let location = JSON.object [ "column" .= (13 :: Int)
, "line" .= (1 :: Int) ]
let err = JSON.object [ "locations" .= JSON.Array (V.singleton location)
, "message" .= ("Unknown Argument \"id\" on Field \"languages\"." :: Text) ]
let expected = JSON.object [ "errors" .= JSON.Array (V.singleton err) ]
protectedNewError token "POST" "/gql" query `matchSimpleJSON` expected
it "returns the old error (though this is deprecated)" $ \((testEnv, port), app) -> do it "returns the old error (though this is deprecated)" $ \((testEnv, port), app) -> do
withApplication app $ do withApplication app $ do
mue <- getMasterUserEnvOrFail testEnv mue <- getMasterUserEnvOrFail testEnv
withValidLogin port (userName mue) (GargPassword $ secretKey mue) $ \token -> do withValidLogin port (userName mue) (GargPassword $ secretKey mue) $ \token -> do
let query = [r| { "query": "{ languages(id:5) { lt_lang } }" } |] let query' = "{ languages(id:5) { lt_lang } }" :: Text
let expected = [json| {"errors": [{"locations":[{"column":13,"line":1}],"message":"Unknown Argument \"id\" on Field \"languages\"."}] } |] let query = JSON.encode $ JSON.object [ "query" .= query' ]
protected token "POST" "/gql" query `shouldRespondWithFragment` expected let location = JSON.object [ "column" .= (13 :: Int)
, "line" .= (1 :: Int) ]
let err = JSON.object [ "locations" .= JSON.Array (V.singleton location)
, "message" .= ("Unknown Argument \"id\" on Field \"languages\"." :: Text) ]
let expected = JSON.object [ "errors" .= JSON.Array (V.singleton err) ]
protected token "POST" "/gql" query `matchSimpleJSON` expected
it "check new errors with 'type'" $ \((testEnv, port), app) -> do it "check new errors with 'type'" $ \((testEnv, port), app) -> do
withApplication app $ do withApplication app $ do
......
...@@ -16,6 +16,7 @@ module Test.API.Private ( ...@@ -16,6 +16,7 @@ module Test.API.Private (
, protectedWith , protectedWith
) where ) where
import Data.Aeson ((.=))
import Data.Aeson qualified as JSON import Data.Aeson qualified as JSON
import Data.ByteString.Lazy qualified as L import Data.ByteString.Lazy qualified as L
import Data.ByteString.Lazy.Char8 qualified as C8L import Data.ByteString.Lazy.Char8 qualified as C8L
...@@ -36,12 +37,12 @@ import Servant.Auth.Client () ...@@ -36,12 +37,12 @@ import Servant.Auth.Client ()
import Servant.Auth.Client qualified as SA import Servant.Auth.Client qualified as SA
import Servant.Client import Servant.Client
import Test.API.Authentication (auth_api) import Test.API.Authentication (auth_api)
import Test.API.Setup (withTestDBAndPort, mkUrl, createAliceAndBob) import Test.API.Setup (withTestDBAndPort, mkUrl)
import Test.Database.Setup (getUserOrFail)
import Test.Hspec import Test.Hspec
import Test.Hspec.Wai hiding (pendingWith) import Test.Hspec.Wai hiding (pendingWith)
import Test.Hspec.Wai.Internal (withApplication) import Test.Hspec.Wai.Internal (withApplication)
import Test.Hspec.Wai.JSON (json) import Test.Utils (matchSimpleJSON)
import Test.Utils (shouldRespondWithFragment)
-- | Issue a request with a valid 'Authorization: Bearer' inside. -- | Issue a request with a valid 'Authorization: Bearer' inside.
protected :: HasCallStack protected :: HasCallStack
...@@ -125,13 +126,10 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -125,13 +126,10 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
manager <- runIO $ newManager defaultManagerSettings manager <- runIO $ newManager defaultManagerSettings
let clientEnv port = mkClientEnv manager (baseUrl { baseUrlPort = port }) let clientEnv port = mkClientEnv manager (baseUrl { baseUrlPort = port })
-- around setupAliceAndBob $ describe "GET /api/v1.0/user" $ do
describe "GET /api/v1.0/user" $ do describe "GET /api/v1.0/user" $ do
-- FIXME(adn): unclear if this is useful at all. Doesn't do permission checking. -- FIXME(adn): unclear if this is useful at all. Doesn't do permission checking.
it "doesn't allow someone with an invalid token to show the results" $ \((testEnv, port), _) -> do it "doesn't allow someone with an invalid token to show the results" $ \((_testEnv, port), _) -> do
_ <- createAliceAndBob testEnv
let ( roots_api :<|> _nodes_api let ( roots_api :<|> _nodes_api
) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token "bogus") ) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token "bogus")
...@@ -141,9 +139,10 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -141,9 +139,10 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
length result `shouldBe` 0 length result `shouldBe` 0
-- FIXME(adn): unclear if this is useful at all. Doesn't do permission checking. -- FIXME(adn): unclear if this is useful at all. Doesn't do permission checking.
it "allows 'alice' to see the results" $ \((_testEnv, port), _) -> do it "allows 'alice' to see the results" $ \((testEnv, port), _) -> do
(_aliceEmail, alicePassword, _aliceId, _aliceNodeId) <- getUserOrFail testEnv "alice"
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
let ( roots_api :<|> _nodes_api let ( roots_api :<|> _nodes_api
) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token $ TE.encodeUtf8 $ token) ) = client (Proxy :: Proxy (MkProtectedAPI GargAdminAPI)) (SA.Token $ TE.encodeUtf8 $ token)
let ( admin_user_api_get :<|> _) = roots_api let ( admin_user_api_get :<|> _) = roots_api
...@@ -153,31 +152,51 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -153,31 +152,51 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
describe "GET /api/v1.0/node" $ do describe "GET /api/v1.0/node" $ do
it "unauthorised users shouldn't see anything" $ \((_testEnv, port), app) -> do it "unauthorised users shouldn't see anything" $ \((testEnv, port), app) -> do
(_aliceEmail, _alicePassword, _aliceId, aliceNodeId) <- getUserOrFail testEnv "alice"
(_bobEmail, _bobPassword, _bobId, bobNodeId) <- getUserOrFail testEnv "bob"
withApplication app $ do withApplication app $ do
get (mkUrl port "/node/1") `shouldRespondWith` 401 get (mkUrl port "/node/" <> show aliceNodeId) `shouldRespondWith` 401
get (mkUrl port "/node/" <> show bobNodeId) `shouldRespondWith` 401
it "allows 'alice' to see her own node info" $ \((_testEnv, port), app) -> do it "allows 'alice' to see her own node info" $ \((testEnv, port), app) -> do
(_aliceEmail, alicePassword, aliceId, aliceNodeId) <- getUserOrFail testEnv "alice"
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
protected token "GET" (mkUrl port "/node/8") "" protected token "GET" (mkUrl port "/node/" <> show aliceNodeId) ""
`shouldRespondWithFragment` [json| {"id":8,"user_id":2,"name":"alice" } |] `matchSimpleJSON`
(JSON.object [ "id" .= aliceNodeId
, "user_id" .= aliceId
, "name" .= ("alice" :: Text) ])
it "forbids 'alice' to see others node private info" $ \((testEnv, port), app) -> do
(_aliceEmail, alicePassword, _aliceId, _aliceNodeId) <- getUserOrFail testEnv "alice"
(_bobEmail, _bobPassword, _bobId, bobNodeId) <- getUserOrFail testEnv "bob"
it "forbids 'alice' to see others node private info" $ \((_testEnv, port), app) -> do
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
protected token "GET" (mkUrl port "/node/1") "" `shouldRespondWith` 403 protected token "GET" (mkUrl port "/node/" <> show bobNodeId) "" `shouldRespondWith` 403
describe "GET /api/v1.0/tree" $ do describe "GET /api/v1.0/tree" $ do
it "unauthorised users shouldn't see anything" $ \((_testEnv, port), app) -> do it "unauthorised users shouldn't see anything" $ \((testEnv, port), app) -> do
(_aliceEmail, _alicePassword, _aliceId, aliceNodeId) <- getUserOrFail testEnv "alice"
withApplication app $ do withApplication app $ do
get (mkUrl port "/tree/1") `shouldRespondWith` 401 get (mkUrl port "/tree/" <> show aliceNodeId) `shouldRespondWith` 401
it "allows 'alice' to see her own node info" $ \((_testEnv, port), app) -> do it "allows 'alice' to see her own node info" $ \((testEnv, port), app) -> do
(_aliceEmail, alicePassword, _aliceId, aliceNodeId) <- getUserOrFail testEnv "alice"
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
protected token "GET" (mkUrl port "/tree/8") "" protected token "GET" (mkUrl port "/tree/" <> show aliceNodeId) ""
`shouldRespondWithFragment` [json| { "node": {"id":8, "name":"alice", "type": "NodeUser" } } |] `matchSimpleJSON` -- [json| { "node": {"id":8, "name":"alice", "type": "NodeUser" } } |]
(JSON.object [ "node" .= JSON.object [ "id" .= aliceNodeId
, "name" .= ("alice" :: Text)
, "type" .= ("NodeUser" :: Text)]
])
it "forbids 'alice' to see others node private info" $ \((_testEnv, port), app) -> do it "forbids 'alice' to see others node private info" $ \((_testEnv, port), app) -> do
withApplication app $ do withApplication app $ do
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
module Test.API.Setup where module Test.API.Setup where
-- import Gargantext.Prelude (printDebug) import Control.Exception (bracket)
import Control.Lens import Control.Lens
import Control.Monad.Reader import Control.Monad.Reader
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.Map.Strict qualified as Map
import Fmt (Builder, (+|), (|+)) import Fmt (Builder, (+|), (|+))
import Gargantext.API (makeApp) import Gargantext.API (makeApp)
import Gargantext.API.Admin.EnvTypes (Mode(Mock), Env (..)) import Gargantext.API.Admin.EnvTypes (Mode(Mock), Env (..))
...@@ -14,12 +15,15 @@ import Gargantext.API.Admin.Settings ...@@ -14,12 +15,15 @@ import Gargantext.API.Admin.Settings
import Gargantext.API.Admin.Types import Gargantext.API.Admin.Types
import Gargantext.API.Errors.Types import Gargantext.API.Errors.Types
import Gargantext.API.Prelude import Gargantext.API.Prelude
import Gargantext.Core.Mail (EmailAddress)
import Gargantext.Core.NLP import Gargantext.Core.NLP
import Gargantext.Core.NodeStory import Gargantext.Core.NodeStory
import Gargantext.Core.Types (UserId) import Gargantext.Core.Types (UserId)
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Action.User.New import Gargantext.Database.Action.User.New
import Gargantext.Database.Admin.Types.Node (NodeId(..))
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
import Gargantext.Database.Query.Tree.Root (getRootId)
import Gargantext.Prelude.Config import Gargantext.Prelude.Config
import Gargantext.Prelude.Mail qualified as Mail import Gargantext.Prelude.Mail qualified as Mail
import Gargantext.Prelude.NLP qualified as NLP import Gargantext.Prelude.NLP qualified as NLP
...@@ -82,17 +86,41 @@ withGargApp :: Application -> (Warp.Port -> IO ()) -> IO () ...@@ -82,17 +86,41 @@ withGargApp :: Application -> (Warp.Port -> IO ()) -> IO ()
withGargApp app action = do withGargApp app action = do
Warp.testWithApplication (pure app) action Warp.testWithApplication (pure app) action
withAliceAndBob :: (TestEnv -> IO ()) -> IO ()
withAliceAndBob action =
withTestDBWithTriggers $ \testEnv -> do
bracket (setupAliceAndBob testEnv) (removeAliceAndBob) action
where
setupAliceAndBob testEnv = do
testEnvAlice <- createUser testEnv "alice@gargan.text" (GargPassword "alice")
testEnvAliceBob <- createUser testEnvAlice "bob@gargan.text" (GargPassword "bob")
pure testEnvAliceBob
removeAliceAndBob _ = do
-- TODO
pure ()
withTestDBAndPort :: (((TestEnv, Warp.Port), Application) -> IO ()) -> IO () withTestDBAndPort :: (((TestEnv, Warp.Port), Application) -> IO ()) -> IO ()
withTestDBAndPort action = withTestDBAndPort action =
withTestDBWithTriggers $ \testEnv -> do withAliceAndBob $ \testEnv -> do
app <- withLoggerHoisted Mock $ \ioLogger -> do app <- withLoggerHoisted Mock $ \ioLogger -> do
env <- newTestEnv testEnv ioLogger 8080 env <- newTestEnv testEnv ioLogger 8080
makeApp env makeApp env
withGargApp app $ \port -> withGargApp app $ \port -> do
action ((testEnv, port), app) action ((testEnv, port), app)
-- | Creates two users, Alice & Bob. Alice shouldn't be able to see -- | Creates two users, Alice & Bob. Alice shouldn't be able to see
-- Bob's private data and vice-versa. -- Bob's private data and vice-versa.
createUser :: TestEnv -> EmailAddress -> GargPassword -> IO TestEnv
createUser testEnv email pass = do
flip runReaderT testEnv $ runTestMonad $ do
let nur = mkNewUser email pass
let NewUser username _ _ = nur
userId <- new_user nur
rootId <- getRootId (UserName username)
pure $ testEnv { test_users = Map.insert username (email, pass, userId, _NodeId rootId) $ test_users testEnv }
createAliceAndBob :: TestEnv -> IO (UserId, UserId) createAliceAndBob :: TestEnv -> IO (UserId, UserId)
createAliceAndBob testEnv = do createAliceAndBob testEnv = do
flip runReaderT testEnv $ runTestMonad $ do flip runReaderT testEnv $ runTestMonad $ do
......
...@@ -35,7 +35,8 @@ import Gargantext.Prelude hiding (get) ...@@ -35,7 +35,8 @@ import Gargantext.Prelude hiding (get)
import Network.Wai.Handler.Warp qualified as Wai import Network.Wai.Handler.Warp qualified as Wai
import Paths_gargantext (getDataFileName) import Paths_gargantext (getDataFileName)
import Test.API.Private (withValidLogin, protectedJSON, postJSONUrlEncoded, getJSON) import Test.API.Private (withValidLogin, protectedJSON, postJSONUrlEncoded, getJSON)
import Test.API.Setup (withTestDBAndPort, mkUrl, createAliceAndBob) import Test.API.Setup (withTestDBAndPort, mkUrl)
import Test.Database.Setup (getUserOrFail)
import Test.Database.Types import Test.Database.Types
import Test.Hspec import Test.Hspec
import Test.Hspec.Wai.Internal (withApplication, WaiSession) import Test.Hspec.Wai.Internal (withApplication, WaiSession)
...@@ -101,15 +102,14 @@ pollUntilFinished tkn port mkUrlPiece = go 60 ...@@ -101,15 +102,14 @@ pollUntilFinished tkn port mkUrlPiece = go 60
tests :: Spec tests :: Spec
tests = sequential $ aroundAll withTestDBAndPort $ do tests = sequential $ aroundAll withTestDBAndPort $ do
describe "UpdateList API" $ do describe "UpdateList API" $ do
it "setup DB triggers and users" $ \((testEnv, _), _) -> do
void $ createAliceAndBob testEnv
describe "POST /api/v1.0/lists/:id/add/form/async (JSON)" $ do describe "POST /api/v1.0/lists/:id/add/form/async (JSON)" $ do
it "allows uploading a JSON ngrams file" $ \((testEnv, port), app) -> do it "allows uploading a JSON ngrams file" $ \((testEnv, port), app) -> do
(_aliceEmail, alicePassword, _aliceId, _aliceNodeId) <- getUserOrFail testEnv "alice"
cId <- newCorpusForUser testEnv "alice" cId <- newCorpusForUser testEnv "alice"
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|] ([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|]
-- Upload the JSON doc -- Upload the JSON doc
simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName "test-data/ngrams/simple.json") simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName "test-data/ngrams/simple.json")
...@@ -150,9 +150,11 @@ tests = sequential $ aroundAll withTestDBAndPort $ do ...@@ -150,9 +150,11 @@ tests = sequential $ aroundAll withTestDBAndPort $ do
])]) ])])
it "allows uploading a CSV ngrams file" $ \((testEnv, port), app) -> do it "allows uploading a CSV ngrams file" $ \((testEnv, port), app) -> do
(_aliceEmail, alicePassword, _aliceId, _aliceNodeId) <- getUserOrFail testEnv "alice"
cId <- newCorpusForUser testEnv "alice" cId <- newCorpusForUser testEnv "alice"
withApplication app $ do withApplication app $ do
withValidLogin port "alice" (GargPassword "alice") $ \token -> do withValidLogin port "alice" alicePassword $ \token -> do
([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|] ([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|]
-- Upload the CSV doc -- Upload the CSV doc
simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName "test-data/ngrams/simple.csv") simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName "test-data/ngrams/simple.csv")
......
...@@ -3,11 +3,13 @@ module Test.Database.Setup ( ...@@ -3,11 +3,13 @@ module Test.Database.Setup (
withTestDB withTestDB
, withTestDBWithTriggers , withTestDBWithTriggers
, getMasterUserEnvOrFail , getMasterUserEnvOrFail
, getUserOrFail
, fakeIniPath , fakeIniPath
, testEnvToPgConnectionInfo , testEnvToPgConnectionInfo
, MasterUserEnv(..) , MasterUserEnv(..)
) where ) where
import Data.Map.Strict qualified as Map
import Data.Pool hiding (withResource) import Data.Pool hiding (withResource)
import Data.Pool qualified as Pool import Data.Pool qualified as Pool
import Data.String (fromString) import Data.String (fromString)
...@@ -18,13 +20,15 @@ import Database.PostgreSQL.Simple.Options qualified as Client ...@@ -18,13 +20,15 @@ import Database.PostgreSQL.Simple.Options qualified as Client
import Database.PostgreSQL.Simple.Options qualified as Opts import Database.PostgreSQL.Simple.Options qualified as Opts
import Database.Postgres.Temp qualified as Tmp import Database.Postgres.Temp qualified as Tmp
import Gargantext.API.Admin.EnvTypes (Mode(Mock)) import Gargantext.API.Admin.EnvTypes (Mode(Mock))
import Gargantext.Core.Mail (EmailAddress)
import Gargantext.Core.NodeStory (fromDBNodeStoryEnv) import Gargantext.Core.NodeStory (fromDBNodeStoryEnv)
import Gargantext.Core.Types.Individu (GargPassword(..), User(..)) import Gargantext.Core.Types.Individu (GargPassword(..), User(..), Username)
import Gargantext.Database.Action.Flow (getOrMk_RootWithCorpus) import Gargantext.Database.Action.Flow (getOrMk_RootWithCorpus)
import Gargantext.Database.Action.User.New (mkNewUser, new_user) import Gargantext.Database.Action.User.New (mkNewUser, new_user)
import Gargantext.Database.Admin.Config (userMaster, corpusMasterName) import Gargantext.Database.Admin.Config (userMaster, corpusMasterName)
import Gargantext.Database.Admin.Trigger.Init (initFirstTriggers, initLastTriggers) import Gargantext.Database.Admin.Trigger.Init (initFirstTriggers, initLastTriggers)
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataCorpus) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataCorpus)
import Gargantext.Database.Admin.Types.Node (NodeId, UserId)
import Gargantext.Database.Query.Table.Node (getOrMkList) import Gargantext.Database.Query.Table.Node (getOrMkList)
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config import Gargantext.Prelude.Config
...@@ -91,7 +95,8 @@ setup = do ...@@ -91,7 +95,8 @@ setup = do
, test_nodeStory , test_nodeStory
, test_usernameGen = ugen , test_usernameGen = ugen
, test_logger = logger , test_logger = logger
, test_masterUserEnv = Nothing } , test_masterUserEnv = Nothing
, test_users = mempty }
withTestDB :: (TestEnv -> IO ()) -> IO () withTestDB :: (TestEnv -> IO ()) -> IO ()
withTestDB = bracket setup teardown withTestDB = bracket setup teardown
...@@ -117,7 +122,7 @@ setupEnvironment env = flip runReaderT env $ runTestMonad $ do ...@@ -117,7 +122,7 @@ setupEnvironment env = flip runReaderT env $ runTestMonad $ do
withTestDBWithTriggers :: (TestEnv -> IO ()) -> IO () withTestDBWithTriggers :: (TestEnv -> IO ()) -> IO ()
withTestDBWithTriggers action = withTestDB $ \testEnv -> do withTestDBWithTriggers action = withTestDB $ \testEnv -> do
(bracket (setupTriggers testEnv) (const $ pure ()) action) bracket (setupTriggers testEnv) (const $ pure ()) action
where where
setupTriggers testEnv = do setupTriggers testEnv = do
masterUserEnv <- setupEnvironment testEnv masterUserEnv <- setupEnvironment testEnv
...@@ -128,6 +133,12 @@ getMasterUserEnvOrFail (TestEnv { test_masterUserEnv = Nothing }) = ...@@ -128,6 +133,12 @@ getMasterUserEnvOrFail (TestEnv { test_masterUserEnv = Nothing }) =
liftIO $ assertFailure "MasterUserEnv not initialized" liftIO $ assertFailure "MasterUserEnv not initialized"
getMasterUserEnvOrFail (TestEnv { test_masterUserEnv = Just mue }) = pure mue getMasterUserEnvOrFail (TestEnv { test_masterUserEnv = Just mue }) = pure mue
getUserOrFail :: (MonadIO m) => TestEnv -> Username -> m (EmailAddress, GargPassword, UserId, Int)
getUserOrFail (TestEnv { test_users }) username =
case Map.lookup username test_users of
Nothing -> liftIO $ assertFailure ("user " <> T.unpack username <> " not initialized")
Just u -> pure u
testEnvToPgConnectionInfo :: TestEnv -> PG.ConnectInfo testEnvToPgConnectionInfo :: TestEnv -> PG.ConnectInfo
testEnvToPgConnectionInfo TestEnv{..} = testEnvToPgConnectionInfo TestEnv{..} =
PG.ConnectInfo { PG.connectHost = "0.0.0.0" PG.ConnectInfo { PG.connectHost = "0.0.0.0"
......
...@@ -30,10 +30,12 @@ import Gargantext.API.Admin.EnvTypes ...@@ -30,10 +30,12 @@ import Gargantext.API.Admin.EnvTypes
import Gargantext.API.Admin.Orchestrator.Types import Gargantext.API.Admin.Orchestrator.Types
import Gargantext.API.Errors.Types import Gargantext.API.Errors.Types
import Gargantext.API.Prelude import Gargantext.API.Prelude
import Gargantext.Core.Mail (EmailAddress)
import Gargantext.Core.Mail.Types (HasMail(..)) import Gargantext.Core.Mail.Types (HasMail(..))
import Gargantext.Core.NLP (HasNLPServer(..)) import Gargantext.Core.NLP (HasNLPServer(..))
import Gargantext.Core.NodeStory import Gargantext.Core.NodeStory
import Gargantext.Database.Admin.Types.Node (CorpusId, ListId, UserId) import Gargantext.Core.Types.Individu (Username, GargPassword)
import Gargantext.Database.Admin.Types.Node (CorpusId, ListId, NodeId, UserId)
import Gargantext.Database.Prelude (HasConfig(..), HasConnectionPool(..)) import Gargantext.Database.Prelude (HasConfig(..), HasConnectionPool(..))
import Gargantext.Database.Query.Table.Node.Error import Gargantext.Database.Query.Table.Node.Error
import Gargantext.Prelude.Config import Gargantext.Prelude.Config
...@@ -73,6 +75,8 @@ data TestEnv = TestEnv { ...@@ -73,6 +75,8 @@ data TestEnv = TestEnv {
-- NOTE Maybe it's better to do a 2-step process with TestEnv', TestEnv -- NOTE Maybe it's better to do a 2-step process with TestEnv', TestEnv
-- but it seems a bigger rewrite -- but it seems a bigger rewrite
, test_masterUserEnv :: !(Maybe MasterUserEnv) , test_masterUserEnv :: !(Maybe MasterUserEnv)
, test_users :: !(Map Username (EmailAddress, GargPassword, UserId, Int))
} }
newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a } newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a }
......
...@@ -10,11 +10,15 @@ import Data.Aeson ...@@ -10,11 +10,15 @@ import Data.Aeson
import Data.Aeson qualified as JSON import Data.Aeson qualified as JSON
import Data.Aeson.KeyMap qualified as KM import Data.Aeson.KeyMap qualified as KM
import Data.ByteString.Char8 qualified as B import Data.ByteString.Char8 qualified as B
import Data.ByteString.Lazy qualified as BSL
import Data.Char (isSpace) import Data.Char (isSpace)
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Network.HTTP.Types import Network.HTTP.Types
import Network.Wai.Test import Network.Wai.Test
import Prelude import Prelude
import Test.Hspec.Expectations import Test.Hspec.Expectations
import Test.Hspec.Expectations.Json (shouldMatchJson)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON (FromValue(..)) import Test.Hspec.Wai.JSON (FromValue(..))
import Test.Hspec.Wai.Matcher import Test.Hspec.Wai.Matcher
...@@ -49,7 +53,7 @@ shouldRespondWithFragmentCustomStatus :: HasCallStack ...@@ -49,7 +53,7 @@ shouldRespondWithFragmentCustomStatus :: HasCallStack
shouldRespondWithFragmentCustomStatus status action matcher = do shouldRespondWithFragmentCustomStatus status action matcher = do
let m = (getJsonMatcher matcher) { matchStatus = status } let m = (getJsonMatcher matcher) { matchStatus = status }
r <- action r <- action
forM_ (match r (getJsonMatcher $ JsonFragmentResponseMatcher m)) (liftIO . expectationFailure) forM_ (match r m) (liftIO . expectationFailure)
instance FromValue JsonFragmentResponseMatcher where instance FromValue JsonFragmentResponseMatcher where
...@@ -89,3 +93,18 @@ containsJSON expected = MatchBody matcher ...@@ -89,3 +93,18 @@ containsJSON expected = MatchBody matcher
isSubsetOf (Object sub) (Object sup) = isSubsetOf (Object sub) (Object sup) =
all (\(key, value) -> KM.lookup key sup == Just value) (KM.toList sub) all (\(key, value) -> KM.lookup key sup == Just value) (KM.toList sub)
isSubsetOf x y = x == y isSubsetOf x y = x == y
matchSimpleJSON :: WaiSession st SResponse
-> Value
-> WaiExpectation st
matchSimpleJSON action value = do
SResponse { simpleStatus
, simpleBody } <- action
let fail' = liftIO . expectationFailure
if simpleStatus /= status200 then
fail' $ "status mismatch: expected 200, but got " ++ (show simpleStatus)
else
pure ()
case JSON.decode simpleBody of
Nothing -> fail' $ "can't decode json for " ++ (T.unpack $ TE.decodeUtf8 $ BSL.toStrict simpleBody)
Just sb -> liftIO $ sb `shouldMatchJson` value
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment