@@ -27,6 +27,7 @@ spec :: Spec
27
27
spec = describe " Servant.Server.Internal.Router" $ do
28
28
routerSpec
29
29
distributivitySpec
30
+ serverLayoutSpec
30
31
31
32
routerSpec :: Spec
32
33
routerSpec = do
@@ -101,12 +102,28 @@ distributivitySpec =
101
102
it " properly handles mixing static paths at different levels" $ do
102
103
level `shouldHaveSameStructureAs` levelRef
103
104
105
+ serverLayoutSpec :: Spec
106
+ serverLayoutSpec =
107
+ describe " serverLayout" $ do
108
+ it " correctly represents the example API" $ do
109
+ exampleLayout `shouldHaveLayout` expectedExampleLayout
110
+ it " aggregates capture hints when different" $ do
111
+ dynamic `shouldHaveLayout` expectedDynamicLayout
112
+ it " nubs capture hints when equal" $ do
113
+ dynamicSameType `shouldHaveLayout` expectedDynamicSameTypeLayout
114
+
104
115
shouldHaveSameStructureAs ::
105
116
(HasServer api1 '[] , HasServer api2 '[] ) => Proxy api1 -> Proxy api2 -> Expectation
106
117
shouldHaveSameStructureAs p1 p2 =
107
118
unless (sameStructure (makeTrivialRouter p1) (makeTrivialRouter p2)) $
108
119
expectationFailure (" expected:\n " ++ unpack (layout p2) ++ " \n but got:\n " ++ unpack (layout p1))
109
120
121
+ shouldHaveLayout ::
122
+ (HasServer api '[] ) => Proxy api -> Text -> Expectation
123
+ shouldHaveLayout p l =
124
+ unless (routerLayout (makeTrivialRouter p) == l) $
125
+ expectationFailure (" expected:\n " ++ unpack l ++ " \n but got:\n " ++ unpack (layout p))
126
+
110
127
makeTrivialRouter :: (HasServer layout '[] ) => Proxy layout -> Router ()
111
128
makeTrivialRouter p =
112
129
route p EmptyContext (emptyDelayed (FailFatal err501))
@@ -342,3 +359,74 @@ level = Proxy
342
359
343
360
levelRef :: Proxy LevelRef
344
361
levelRef = Proxy
362
+
363
+ -- The example API for the 'layout' function.
364
+ -- Should get factorized by the 'choice' smart constructor.
365
+ type ExampleLayout =
366
+ " a" :> " d" :> Get '[JSON ] NoContent
367
+ :<|> " b" :> Capture " x" Int :> Get '[JSON ] Bool
368
+ :<|> " c" :> Put '[JSON ] Bool
369
+ :<|> " a" :> " e" :> Get '[JSON ] Int
370
+ :<|> " b" :> Capture " x" Int :> Put '[JSON ] Bool
371
+ :<|> Raw
372
+
373
+ exampleLayout :: Proxy ExampleLayout
374
+ exampleLayout = Proxy
375
+
376
+ -- The expected representation of the example API layout
377
+ --
378
+ expectedExampleLayout :: Text
379
+ expectedExampleLayout =
380
+ " /\n \
381
+ \├─ a/\n \
382
+ \│ ├─ d/\n \
383
+ \│ │ └─•\n \
384
+ \│ └─ e/\n \
385
+ \│ └─•\n \
386
+ \├─ b/\n \
387
+ \│ └─ <x::CaptureSingle>/\n \
388
+ \│ ├─•\n \
389
+ \│ ┆\n \
390
+ \│ └─•\n \
391
+ \├─ c/\n \
392
+ \│ └─•\n \
393
+ \┆\n \
394
+ \└─ <raw>\n "
395
+
396
+ -- The expected representation of the Dynamic API layout.
397
+ --
398
+ expectedDynamicLayout :: Text
399
+ expectedDynamicLayout =
400
+ " /\n \
401
+ \└─ a/\n \
402
+ \ └─ <foo::CaptureSingle|bar::CaptureSingle|baz::CaptureSingle>/\n \
403
+ \ ├─ b/\n \
404
+ \ │ └─•\n \
405
+ \ ├─ c/\n \
406
+ \ │ └─•\n \
407
+ \ └─ d/\n \
408
+ \ └─•\n "
409
+
410
+ -- The same Dynamic API as above, except that the captured
411
+ -- values have the same hints
412
+ type DynamicSameType =
413
+ " a" :> Capture " foo" Int :> " b" :> End
414
+ :<|> " a" :> Capture " foo" Int :> " c" :> End
415
+ :<|> " a" :> Capture " foo" Int :> " d" :> End
416
+
417
+ dynamicSameType :: Proxy DynamicSameType
418
+ dynamicSameType = Proxy
419
+
420
+ -- The expected representation of the DynamicSameType API layout.
421
+ --
422
+ expectedDynamicSameTypeLayout :: Text
423
+ expectedDynamicSameTypeLayout =
424
+ " /\n \
425
+ \└─ a/\n \
426
+ \ └─ <foo::CaptureSingle>/\n \
427
+ \ ├─ b/\n \
428
+ \ │ └─•\n \
429
+ \ ├─ c/\n \
430
+ \ │ └─•\n \
431
+ \ └─ d/\n \
432
+ \ └─•\n "
0 commit comments