module Servant.Aeson.Internal where
import Data.Aeson
import Data.Function
import Data.List
import Data.Proxy
import Data.Typeable
import GHC.TypeLits
import Servant.API
import Test.Hspec
import Test.QuickCheck
import Test.Aeson.Internal.GoldenSpecs
import Test.Aeson.Internal.RoundtripSpecs
import Test.Aeson.GenericSpecs
apiRoundtripSpecs :: (HasGenericSpecs api) => Proxy api -> Spec
apiRoundtripSpecs = sequence_ . map roundtrip . mkRoundtripSpecs defaultSettings
apiGoldenSpecs :: HasGenericSpecs api => Proxy api -> Spec
apiGoldenSpecs proxy = apiGoldenSpecsWithSettings defaultSettings proxy
apiGoldenSpecsWithSettings :: HasGenericSpecs api => Settings -> Proxy api -> Spec
apiGoldenSpecsWithSettings settings proxy = sequence_ $ map golden $ mkRoundtripSpecs settings proxy
apiSpecs :: (HasGenericSpecs api) => Proxy api -> Spec
apiSpecs proxy = apiSpecsWithSettings defaultSettings proxy
apiSpecsWithSettings :: (HasGenericSpecs api) => Settings -> Proxy api -> Spec
apiSpecsWithSettings settings proxy = sequence_ $ map (\ ts -> roundtrip ts >> golden ts) $ mkRoundtripSpecs settings proxy
usedTypes :: (HasGenericSpecs api) => Proxy api -> [TypeRep]
usedTypes = map typ . mkRoundtripSpecs defaultSettings
mkRoundtripSpecs :: (HasGenericSpecs api) => Settings -> Proxy api -> [TypeSpec]
mkRoundtripSpecs settings = normalize . collectRoundtripSpecs settings
where
normalize = nubBy ((==) `on` typ) . sortBy (compare `on` (show . typ))
class HasGenericSpecs api where
collectRoundtripSpecs :: Settings -> Proxy api -> [TypeSpec]
instance (HasGenericSpecs a, HasGenericSpecs b) => HasGenericSpecs (a :<|> b) where
collectRoundtripSpecs settings Proxy =
collectRoundtripSpecs settings (Proxy :: Proxy a) ++
collectRoundtripSpecs settings (Proxy :: Proxy b)
#if MIN_VERSION_servant(0, 5, 0)
instance
(MkTypeSpecs response) =>
HasGenericSpecs (Verb (method :: StdMethod) returnStatus contentTypes response) where
collectRoundtripSpecs settings Proxy = do
mkTypeSpecs settings (Proxy :: Proxy response)
instance
HasGenericSpecs (Verb (method :: StdMethod) returnStatus contentTypes NoContent) where
collectRoundtripSpecs _ Proxy = []
instance (MkTypeSpecs response) =>
HasGenericSpecs (Verb (method :: StdMethod) returnStatus contentTypes (Headers hs response)) where
collectRoundtripSpecs settings Proxy = mkTypeSpecs settings (Proxy :: Proxy response)
#else
instance
(MkTypeSpecs response) =>
HasGenericSpecs (Get contentTypes response) where
collectRoundtripSpecs settings Proxy = do
mkTypeSpecs settings (Proxy :: Proxy response)
instance
(MkTypeSpecs response) =>
HasGenericSpecs (Post contentTypes response) where
collectRoundtripSpecs settings Proxy = mkTypeSpecs settings (Proxy :: Proxy response)
instance
(MkTypeSpecs response) =>
HasGenericSpecs (Get contentTypes (Headers hs response)) where
collectRoundtripSpecs settings Proxy = mkTypeSpecs settings (Proxy :: Proxy response)
instance
(MkTypeSpecs response) =>
HasGenericSpecs (Post contentTypes (Headers hs response)) where
collectRoundtripSpecs settings Proxy = mkTypeSpecs settings (Proxy :: Proxy response)
#endif
instance (MkTypeSpecs body, HasGenericSpecs api) =>
HasGenericSpecs (ReqBody contentTypes body :> api) where
collectRoundtripSpecs settings Proxy =
mkTypeSpecs settings (Proxy :: Proxy body) ++
collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs ((path :: Symbol) :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs (Capture (sym :: Symbol) x :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs (QueryFlag (sym :: Symbol) :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs (QueryParam (sym :: Symbol) x :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs (QueryParams (sym :: Symbol) x :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
instance HasGenericSpecs api => HasGenericSpecs (Header (sym :: Symbol) x :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
#if MIN_VERSION_servant(0, 5, 0)
instance HasGenericSpecs api => HasGenericSpecs (AuthProtect (sym :: Symbol) :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
#else
instance HasGenericSpecs api => HasGenericSpecs (MatrixParam name a :> api) where
collectRoundtripSpecs settings Proxy = collectRoundtripSpecs settings (Proxy :: Proxy api)
#endif
data TypeSpec = TypeSpec {
typ :: TypeRep
, roundtrip :: Spec
, golden :: Spec
}
class MkTypeSpecs a where
mkTypeSpecs :: Settings -> Proxy a -> [TypeSpec]
instance (Typeable a, Eq a, Show a, Arbitrary a, ToJSON a, FromJSON a) => MkTypeSpecs a where
mkTypeSpecs settings proxy = pure $
TypeSpec {
typ = typeRep proxy,
roundtrip = roundtripSpecs proxy,
golden = goldenSpecs settings proxy
}
instance
(Eq a, Show a, Typeable a, Arbitrary a, ToJSON a, FromJSON a) =>
MkTypeSpecs [a] where
mkTypeSpecs settings Proxy = pure $
TypeSpec {
typ = typeRep proxy,
roundtrip = genericAesonRoundtripWithNote proxy (Just note),
golden = goldenSpecsWithNote settings proxy (Just note)
}
where
proxy = Proxy :: Proxy a
note = "(as element-type in [])"
instance
(Eq a, Show a, Typeable a, Arbitrary a, ToJSON a, FromJSON a) =>
MkTypeSpecs (Maybe a) where
mkTypeSpecs settings Proxy = pure $
TypeSpec {
typ = typeRep proxy,
roundtrip = genericAesonRoundtripWithNote proxy (Just note),
golden = goldenSpecsWithNote settings proxy (Just note)
}
where
proxy = Proxy :: Proxy a
note = "(as element-type in Maybe)"
instance
MkTypeSpecs () where
mkTypeSpecs _ Proxy = []