yesod-core-1.7.0.0: test/Route/SubDispatchAritySpec.hs
-- | Unit tests for 'checkNestedSubArity', the guard that gives
-- 'mkYesodSubDispatchInstance' an actionable error (instead of a cryptic kind
-- error) when a parameterized subsite is paired with unparameterized nested
-- route datatypes.
module Route.SubDispatchAritySpec (spec) where
import Test.Hspec
import Yesod.Routes.TH.Internal
( checkNestedSubArity
, arityMismatchMessage
, ArityCallSite(..)
, ArityMismatch(..)
, SubsiteName(..)
, RouteName(..)
, SubsiteArity(..)
, RouteArity(..)
)
-- | The newtype wrappers are positional noise in the tests; this names the
-- call the way the production caller does. 'Nothing' means the arities match;
-- 'Just' carries the rendered mismatch message (phrased for a 'SubsiteCall').
check :: String -> String -> Int -> Int -> Maybe String
check sub route subArgs routeArity =
fmap (arityMismatchMessage SubsiteCall) $
checkNestedSubArity
(SubsiteName sub)
(RouteName route)
(SubsiteArity subArgs)
(RouteArity routeArity)
spec :: Spec
spec = describe "checkNestedSubArity" $ do
it "accepts a monomorphic subsite with an unparameterized nested datatype" $
check "MySub" "NestedR" 0 0 `shouldBe` Nothing
it "accepts a parameterized subsite whose nested datatype carries the param" $
check "MySub" "NestedR" 1 1 `shouldBe` Nothing
it "rejects when the nested datatype carries more params than the subsite" $
-- Over-arity leaves the instance head partially applied (kind
-- @Type -> ...@), so this must be rejected, not silently accepted.
case check "MySub" "NestedR" 1 2 of
Nothing -> expectationFailure "expected an arity mismatch"
Just msg -> do
msg `shouldContain` "`NestedR` has 2 type parameter(s)"
msg `shouldContain` "the subsite `MySub` has 1"
it "rejects a parameterized subsite with an unparameterized nested datatype" $
check "MySub" "NestedR" 1 0 `shouldBe` Just
(concat
[ "mkYesodSubDispatchInstance: the nested route datatype "
, "`NestedR` has 0 type parameter(s), but the subsite `MySub` "
, "has 1. The subroute datatypes must carry exactly the "
, "subsite's type parameter(s) \8212 generate the subsite's "
, "routes with `mkYesodSubDataOpts (setParameterizedSubroute "
, "True defaultOpts) ...` so a parameterized subsite gets "
, "parameterized subroutes."
])
it "names both types in the error message" $
case check "MySub" "NestedR" 1 0 of
Nothing -> expectationFailure "expected a mismatch"
Just msg -> do
msg `shouldContain` "MySub"
msg `shouldContain` "NestedR"
describe "arityMismatchMessage phrasing per call site" $ do
let mismatch = ArityMismatch (SubsiteName "MySub") (RouteName "NestedR")
(SubsiteArity 1) (RouteArity 0)
it "names mkYesodSubDispatchInstance and calls it a subsite for a SubsiteCall" $ do
let msg = arityMismatchMessage SubsiteCall mismatch
msg `shouldContain` "mkYesodSubDispatchInstance"
msg `shouldContain` "subsite"
it "names mkYesod and calls it a site (not a subsite) for a TopLevelCall" $ do
let msg = arityMismatchMessage TopLevelCall mismatch
msg `shouldContain` "mkYesod:"
msg `shouldNotContain` "subsite"
msg `shouldContain` "site"