Skip to content

Commit 9d66e16

Browse files
committed
Add spec for serverLayout
1 parent 77b92d0 commit 9d66e16

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

servant-server/test/Servant/Server/RouterSpec.hs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ spec :: Spec
2929
spec = describe "Servant.Server.Internal.Router" $ do
3030
routerSpec
3131
distributivitySpec
32+
serverLayoutSpec
3233

3334
routerSpec :: Spec
3435
routerSpec = do
@@ -103,12 +104,30 @@ distributivitySpec =
103104
it "properly handles mixing static paths at different levels" $ do
104105
level `shouldHaveSameStructureAs` levelRef
105106

107+
serverLayoutSpec :: Spec
108+
serverLayoutSpec =
109+
describe "serverLayout" $ do
110+
it "correctly represents the example API" $ do
111+
exampleLayout `shouldHaveLayout` expectedExampleLayout
112+
it "aggregates capture hints when different" $ do
113+
captureDifferentTypes `shouldHaveLayout` expectedCaptureDifferentTypes
114+
it "nubs capture hints when equal" $ do
115+
captureSameType `shouldHaveLayout` expectedCaptureSameType
116+
it "properly displays CaptureAll hints" $ do
117+
captureAllLayout `shouldHaveLayout` expectedCaptureAllLayout
118+
106119
shouldHaveSameStructureAs ::
107120
(HasServer api1 '[], HasServer api2 '[]) => Proxy api1 -> Proxy api2 -> Expectation
108121
shouldHaveSameStructureAs p1 p2 =
109122
unless (sameStructure (makeTrivialRouter p1) (makeTrivialRouter p2)) $
110123
expectationFailure ("expected:\n" ++ unpack (layout p2) ++ "\nbut got:\n" ++ unpack (layout p1))
111124

125+
shouldHaveLayout ::
126+
(HasServer api '[]) => Proxy api -> Text -> Expectation
127+
shouldHaveLayout p l =
128+
unless (routerLayout (makeTrivialRouter p) == l) $
129+
expectationFailure ("expected:\n" ++ unpack l ++ "\nbut got:\n" ++ unpack (layout p))
130+
112131
makeTrivialRouter :: (HasServer layout '[]) => Proxy layout -> Router ()
113132
makeTrivialRouter p =
114133
route p EmptyContext (emptyDelayed (FailFatal err501))
@@ -344,3 +363,100 @@ level = Proxy
344363

345364
levelRef :: Proxy LevelRef
346365
levelRef = Proxy
366+
367+
-- The example API for the 'layout' function.
368+
-- Should get factorized by the 'choice' smart constructor.
369+
type ExampleLayout =
370+
"a" :> "d" :> Get '[JSON] NoContent
371+
:<|> "b" :> Capture "x" Int :> Get '[JSON] Bool
372+
:<|> "c" :> Put '[JSON] Bool
373+
:<|> "a" :> "e" :> Get '[JSON] Int
374+
:<|> "b" :> Capture "x" Int :> Put '[JSON] Bool
375+
:<|> Raw
376+
377+
exampleLayout :: Proxy ExampleLayout
378+
exampleLayout = Proxy
379+
380+
-- The expected representation of the example API layout
381+
--
382+
expectedExampleLayout :: Text
383+
expectedExampleLayout =
384+
"/\n\
385+
\├─ a/\n\
386+
\│ ├─ d/\n\
387+
\│ │ └─•\n\
388+
\│ └─ e/\n\
389+
\│ └─•\n\
390+
\├─ b/\n\
391+
\│ └─ <x::Int>/\n\
392+
\│ ├─•\n\
393+
\│ ┆\n\
394+
\│ └─•\n\
395+
\├─ c/\n\
396+
\│ └─•\n\
397+
\┆\n\
398+
\└─ <raw>\n"
399+
400+
-- A capture API with all capture types being the same
401+
--
402+
type CaptureSameType =
403+
"a" :> Capture "foo" Int :> "b" :> End
404+
:<|> "a" :> Capture "foo" Int :> "c" :> End
405+
:<|> "a" :> Capture "foo" Int :> "d" :> End
406+
407+
captureSameType :: Proxy CaptureSameType
408+
captureSameType = Proxy
409+
410+
-- The expected representation of the CaptureSameType API layout.
411+
--
412+
expectedCaptureSameType :: Text
413+
expectedCaptureSameType =
414+
"/\n\
415+
\└─ a/\n\
416+
\ └─ <foo::Int>/\n\
417+
\ ├─ b/\n\
418+
\ │ └─•\n\
419+
\ ├─ c/\n\
420+
\ │ └─•\n\
421+
\ └─ d/\n\
422+
\ └─•\n"
423+
424+
-- A capture API capturing different types
425+
--
426+
type CaptureDifferentTypes =
427+
"a" :> Capture "foo" Int :> "b" :> End
428+
:<|> "a" :> Capture "bar" Bool :> "c" :> End
429+
:<|> "a" :> Capture "baz" Char :> "d" :> End
430+
431+
captureDifferentTypes :: Proxy CaptureDifferentTypes
432+
captureDifferentTypes = Proxy
433+
434+
-- The expected representation of the CaptureDifferentTypes API layout.
435+
--
436+
expectedCaptureDifferentTypes :: Text
437+
expectedCaptureDifferentTypes =
438+
"/\n\
439+
\└─ a/\n\
440+
\ └─ <foo::Int|bar::Bool|baz::Char>/\n\
441+
\ ├─ b/\n\
442+
\ │ └─•\n\
443+
\ ├─ c/\n\
444+
\ │ └─•\n\
445+
\ └─ d/\n\
446+
\ └─•\n"
447+
448+
-- An API with a CaptureAll part
449+
450+
type CaptureAllLayout = "a" :> CaptureAll "foos" Int :> End
451+
452+
captureAllLayout :: Proxy CaptureAllLayout
453+
captureAllLayout = Proxy
454+
455+
-- The expected representation of the CaptureAllLayout API.
456+
--
457+
expectedCaptureAllLayout :: Text
458+
expectedCaptureAllLayout =
459+
"/\n\
460+
\└─ a/\n\
461+
\ └─ <foos::[Int]>/\n\
462+
\ └─•\n"

0 commit comments

Comments
 (0)