reroute-0.5.0.0: test/Web/Routing/SafeRoutingSpec.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
module Web.Routing.SafeRoutingSpec where
import Web.Routing.Combinators
import Web.Routing.Router
import Web.Routing.SafeRouting
import Control.Monad.Identity
import Control.Monad.RWS.Strict
import Data.HVect hiding (singleton)
import Data.List
import Data.Maybe
import Test.Hspec
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
data ReturnVar
= IntVar Int
| StrVar T.Text
| BoolVar Bool
| ListVar [ReturnVar]
deriving (Show, Eq, Read, Ord)
defR :: (Monad m, m ReturnVar ~ x) => Path ts ps -> HVectElim ts x -> RegistryT m ReturnVar middleware Bool m ()
defR path action = hookRoute True (toInternalPath path) (HVectElim' action)
defRAny :: (Monad m, m ReturnVar ~ x) => Path ts ps -> HVectElim ts x -> RegistryT m ReturnVar middleware Bool m ()
defRAny path action = hookRouteAnyMethod (toInternalPath path) (HVectElim' action)
-- TODO: abstract this code, move into AbstractRouter
defSubComponent ::
( Monad m
#if __GLASGOW_HASKELL__ <= 708
, Functor m
#endif
, m ([T.Text] -> ReturnVar) ~ x
)
=> Path ts ps
-> HVectElim ts x
-> RegistryT m ReturnVar middleware Bool m ()
defSubComponent path comp =
do let reqType = True
basePath <- ask
modify $ \rs ->
rs { rs_registry =
let (reg, fb) = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
reg' = insertSubComponent (RouteHandle (basePath </!> toInternalPath path) comp) reg
in HM.insert reqType (reg', fb) (rs_registry rs)
}
spec :: Spec
spec =
describe "SafeRouting Spec" $
do it "should match known routes" $
do checkRoute "" [StrVar "root"]
checkRoute "/" [StrVar "root"]
checkRoute "/bar" [StrVar "bar"]
it "should match any routes" $
do checkRoute' "/any" True [StrVar "any", StrVar "any"] -- two due to hookAny
checkRoute' "/any" False [StrVar "any"]
it "should capture variables in routes" $
do checkRoute "/bar/23/baz" [IntVar 23]
checkRoute "/bar/23/baz/" [IntVar 23]
checkRoute "/bar/23/baz/100" [ListVar [IntVar 23, IntVar 100]]
checkRoute "/bar/23/100" [ListVar [IntVar 23, IntVar 100]]
checkRoute "/entry/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
checkRoute "/entry/bytags/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
checkRoute "/entry/2/rel/3" [ListVar [IntVar 2, IntVar 3]]
it "should handle multiple possible matches correctly" $
do checkRoute "/bar/5" [IntVar 5, StrVar "5"]
checkRoute "/bar/bingo" [StrVar "bar/bingo", StrVar "bingo"]
checkRoute "/entry/1/audit" [IntVar 1,ListVar [IntVar 1,StrVar "audit"]]
it "should have a catch all route" $
do checkRoute "/aslkdjk/asdaskl/aslkjd" [StrVar "aslkdjk/asdaskl/aslkjd"]
checkRoute "/zuiasf/zuiasf" [StrVar "zuiasf/zuiasf"]
it "should hand over remaining path pieces to subcomponents" $
do checkRoute "/subcomponent/blog/foo/bar/nanana" [StrVar "blog:foo?bar?nanana"]
it "should handle wildcard routes" $
do checkRoute "/wildcard/" [StrVar ""]
checkRoute "/wildcard/some/additional/data" [StrVar "some/additional/data"]
where
pieces :: T.Text -> [T.Text]
pieces = filter (not . T.null) . T.splitOn "/"
checkRoute' :: T.Text -> Bool -> [ReturnVar] -> Expectation
checkRoute' r b x =
let matches = handleFun b (pieces r)
in sort (map runIdentity matches) `shouldBe` sort x
checkRoute :: T.Text -> [ReturnVar] -> Expectation
checkRoute = flip checkRoute' True
handleFun :: Bool -> [T.Text] -> [Identity ReturnVar]
handleFun = handleFun'
(_, handleFun', _) = runIdentity (runRegistry handleDefs)
handleDefs =
do defR root $ return (StrVar "root")
defRAny "any" $ pure (StrVar "any")
defR "bar" $ return (StrVar "bar")
defR ("bar" </> var) (return . IntVar)
defR ("bar" </> var </> "baz") (return . IntVar)
defR ("bar" </> var </> "baz" </> var) $ \i i2 ->
return (ListVar [IntVar i, IntVar i2])
defR ("bar" </> var </> var) $ \i i2 ->
return (ListVar [IntVar i, IntVar i2])
defR ("entry" </> var </> var) $ \i st ->
return (ListVar [IntVar i, StrVar st])
defR ("entry/bytags" </> var </> var) $ \i st ->
return (ListVar [IntVar i, StrVar st])
defR ("entry" </> var </> "rel" </> var) $ \i i2 ->
return (ListVar [IntVar i, IntVar i2])
defR ("bar" </> "bingo") $ return (StrVar "bar/bingo")
defR ("bar" </> var) $ (return . StrVar . T.pack)
defR ("entry" </> var </> "audit") (return . IntVar)
defSubComponent ("subcomponent" </> var) $ \name ->
return $ \ps -> StrVar $ name <> ":" <> T.intercalate "?" ps
defR ("wildcard" </> wildcard) $ \rest ->
return $ StrVar rest
hookAny True (return . StrVar . T.intercalate "/")