packages feed

yesod-core-1.7.0.0: test/YesodCoreTest/ParamSubsite/SplitRuntime.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | Runtime coverage for a *parameterized* subsite whose nested routes are
-- /split across modules/ — the combination that had no test before (split was
-- monomorphic-only; parameterized nested-discovery was single-module-only).
--
-- The parent @YesodSubDispatch (PSub subsite) master@ instance is generated
-- here with 'mkYesodSubDispatch', but the nested 'PNestedR' handlers and its
-- 'YesodSubDispatchNested' instance live in the separately compiled
-- "YesodCoreTest.ParamSubsite.SplitNested". Because those handlers are NOT in scope
-- here, this module compiles only if the parent splice *delegates* to the
-- external nested instance rather than inlining it — so the build itself is
-- the split-delegation assertion, and the WAI tests confirm it dispatches.
module YesodCoreTest.ParamSubsite.SplitRuntime
    ( specs
    ) where

import Data.Text (Text)
import Test.Hspec
import qualified Data.ByteString.Lazy as L
import qualified Network.HTTP.Types as H
import Yesod.Core

import YesodCoreTest.ParamSubsite.Data
import YesodCoreTest.ParamSubsite.SplitNested ()  -- brings the split nested instance into scope
import YesodCoreTest.RuntimeHarness (assertRequestFor)

-- | Concrete instantiation of the parameterized subsite.
data ConcretePSub = ConcretePSub

instance PClass ConcretePSub App where
    type PAssoc ConcretePSub = Text
    pValue _ _ = "value"

type ConcretePSubT = PSub ConcretePSub

data App = App

-- Only the FLAT subsite handlers live here; the nested ones are deliberately
-- absent (see module haddock).
getPHomeR :: SubHandlerFor (PSub subsite) master Text
getPHomeR = pure "pHome"

getPItemR :: Int -> SubHandlerFor (PSub subsite) master Text
getPItemR _ = pure "pItem"

-- The parent dispatch. Generated in THIS module (separately from the nested
-- 'PNestedR' instance), so its 'mkYesodSubDispatch' splice must delegate to the
-- imported 'YesodSubDispatchNested (PNestedR subsite)' instance.
instance PClass subsite master => YesodSubDispatch (PSub subsite) master where
    yesodSubDispatch = $(mkYesodSubDispatch resourcesPSub)

mkYesod "App" [parseRoutes|
/ HomeR GET
/p PSubR ConcretePSubT getP
|]

instance Yesod App where
    messageLoggerSource = mempty

getHomeR :: Handler Text
getHomeR = pure "home"

getP :: App -> ConcretePSubT
getP _ = PSub ConcretePSub

sampleRoutes :: [Route ConcretePSubT]
sampleRoutes =
    [ PHomeR
    , PItemR 7
    , PNestedR "abc" PNestedHomeR
    , PNestedR "abc" (PNestedDetailR 9)
    ]

testRequestIO
    :: HasCallStack
    => Int
    -> [Text]
    -> H.Method
    -> Maybe L.ByteString
    -> IO ()
testRequestIO status path method mexpected =
    assertRequestFor App method status path mexpected

specs :: Spec
specs = describe "parameterized subsite, nested routes split across modules" $ do
    describe "parseRoute . renderRoute round-trips" $
        mapM_
            (\r -> it (show r) $ parseRoute (renderRoute r) `shouldBe` Just r)
            sampleRoutes

    describe "WAI dispatch (parent delegates to the split nested instance)" $ do
        it "static leaf (PHomeR)" $
            testRequestIO 200 ["p"] "GET" (Just "pHome")
        it "dynamic leaf (PItemR)" $
            testRequestIO 200 ["p", "item", "7"] "GET" (Just "pItem")
        it "nested parent home (delegated PNestedHomeR)" $
            testRequestIO 200 ["p", "abc", "nested"] "GET" (Just "pNestedHome")
        it "nested parent dynamic leaf (delegated PNestedDetailR)" $
            testRequestIO 200 ["p", "abc", "nested", "detail", "9"] "GET" (Just "pNestedDetail")
        it "404 on unknown nested suffix" $
            testRequestIO 404 ["p", "abc", "nested", "oops"] "GET" Nothing
        it "405 on wrong method" $
            testRequestIO 405 ["p", "abc", "nested"] "POST" Nothing