Skip to content

Commit

Permalink
Strip the tests down instead of duplicating stuff from servant-js
Browse files Browse the repository at this point in the history
  • Loading branch information
erewok committed Feb 14, 2017
1 parent f7267ee commit 43757f5
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 186 deletions.
2 changes: 1 addition & 1 deletion servant-py.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ test-suite servant-py-test
main-is: Spec.hs
other-modules:
Servant.PYSpec
Servant.Custom.PYHeaders
Servant.PY.InternalSpec
build-depends: base
, servant-py
, base-compat
Expand Down
1 change: 1 addition & 0 deletions src/Servant/PY/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ defaultPyIndent = indenter 4
-- to other Ints. Then, to get your indentation, pass `indent` to the created function
indenter :: Int -> Proxy Indent -> Text
indenter width space = mconcat $ width `replicate` (T.pack . symbolVal) space
{-# INLINE indenter #-}


-- Created python Functions can have different return styles
Expand Down
60 changes: 0 additions & 60 deletions test/Servant/Custom/PYHeaders.hs

This file was deleted.

69 changes: 69 additions & 0 deletions test/Servant/PY/InternalSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Servant.PY.InternalSpec where

import Data.Either (isRight)
import Data.Monoid ()
import Data.Monoid.Compat ((<>))
import Data.Proxy
import Data.Text (Text)
import qualified Data.Text as T
import GHC.TypeLits
import Prelude ()
import Prelude.Compat
import Test.Hspec hiding
(shouldContain,
shouldNotContain)
import Test.QuickCheck (Arbitrary (..),
choose, listOf,
property)

import Servant.API.ContentTypes
import Servant.API.Internal.Test.ComprehensiveAPI

import Servant.PY.Internal


customOptions :: CommonGeneratorOptions
customOptions = defCommonGeneratorOptions
{ urlPrefix = "urlForRequesting:9000"
, returnMode = DangerMode
}

spec :: Spec
spec = describe "Servant.PY.Internal" internalSpec

shouldContain :: Text -> Text -> Expectation
a `shouldContain` b = shouldSatisfy a (T.isInfixOf b)

shouldNotContain :: Text -> Text -> Expectation
a `shouldNotContain` b = shouldNotSatisfy a (T.isInfixOf b)

newtype ASCII = ASCII {getASCII :: T.Text} deriving (Show)

instance Arbitrary ASCII where
-- Our arbitrary instance is generating only ASCII, since the language-ecmascript's lexer
-- is currently (October 2016) still a bit naïve
arbitrary = fmap (ASCII . T.pack) $ listOf $ choose (minBound, '\127')
shrink xs = (ASCII . T.pack) <$> shrink (T.unpack $ getASCII xs)


internalSpec :: Spec
internalSpec = describe "Internal" $ do
it "should only indent using whitespace" $
property $ \n -> indenter n indent == mconcat (replicate n (T.pack " "))

-- it "should generate only valid python identifiers for any ASCII route" $ do
-- let parseIdentifier = fmap T.pack ""
-- property $ \x -> let valid = toValidFunctionName $ getASCII x in
-- Right valid == parseIdentifier valid
--
-- it "should generate a valid python identifier when supplied with hyphens, unicode whitespace, non-bmp unicode" $ do
-- toValidFunctionName "a_--a\66352b\6158c\65075" `shouldBe` "a_abc\65075"
152 changes: 27 additions & 125 deletions test/Servant/PYSpec.hs
Original file line number Diff line number Diff line change
@@ -1,86 +1,44 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Servant.PYSpec where

import Data.Either (isRight)
import Data.Monoid ()
import Data.Monoid.Compat ((<>))
import Data.Either (isRight)
import Data.Monoid ()
import Data.Monoid.Compat ((<>))
import Data.Proxy
import Data.Text (Text)
import qualified Data.Text as T
import Prelude ()
import Data.Text (Text)
import qualified Data.Text as T
import GHC.TypeLits
import Prelude ()
import Prelude.Compat
import Test.Hspec hiding (shouldContain, shouldNotContain)
import Test.QuickCheck (Arbitrary (..),
choose, listOf,
property)
import Test.Hspec hiding
(shouldContain,
shouldNotContain)
import Test.QuickCheck (Arbitrary (..),
choose, listOf,
property)

import Servant.API.Internal.Test.ComprehensiveAPI
import Servant.API.ContentTypes
import Servant.PY
import Servant.PY.Internal
import qualified Servant.PY.Requests as R
import Servant.Custom.PYHeaders



-- This declaration simply checks that all instances are in place.
_ = pyForAPI comprehensiveAPIWithoutRaw requests :: Text

-- * specs

type TestAPI = "simple" :> ReqBody '[JSON,FormUrlEncoded] Text :> Post '[JSON] Bool
:<|> "has.extension" :> Get '[FormUrlEncoded,JSON] Bool

type TopLevelRawAPI = "test" :> Get '[JSON] Int
:<|> Raw

type HeaderHandlingAPI = "test" :> Header "Foo" Text
:> Get '[JSON] Int

type CustomAuthAPI = "test" :> Authorization "Basic" Text
:> Get '[JSON] Int

type CustomHeaderAPI = "test" :> ThreeMilePilot Text
:> Get '[JSON] Int

type CustomHeaderAPI2 = "test" :> KlikatatIkatowi Text
:> Get '[JSON] Int

headerHandlingProxy :: Proxy HeaderHandlingAPI
headerHandlingProxy = Proxy

customAuthProxy :: Proxy CustomAuthAPI
customAuthProxy = Proxy

customHeaderProxy :: Proxy CustomHeaderAPI
customHeaderProxy = Proxy
import Servant.API.Internal.Test.ComprehensiveAPI

customHeaderProxy2 :: Proxy CustomHeaderAPI2
customHeaderProxy2 = Proxy
import Servant.PY.Internal

data TestNames = Requests
| RequestsWith
deriving (Show, Eq)

customOptions :: CommonGeneratorOptions
customOptions = defCommonGeneratorOptions
{ urlPrefix = "urlForRequesting:9000"
, returnMode = DangerMode
}
{ urlPrefix = "urlForRequesting:9000"
, returnMode = DangerMode
}

spec :: Spec
spec = describe "Servant.Requests" $ do
generatePYSpec Requests R.requestsWithDef
generatePYSpec RequestsWith (R.requestsWith customOptions)
internalSpec
spec = describe "Servant.PY.Internal" internalSpec

shouldContain :: Text -> Text -> Expectation
a `shouldContain` b = shouldSatisfy a (T.isInfixOf b)
Expand All @@ -91,69 +49,13 @@ a `shouldNotContain` b = shouldNotSatisfy a (T.isInfixOf b)
newtype ASCII = ASCII {getASCII :: T.Text} deriving (Show)

instance Arbitrary ASCII where
-- Our arbitrary instance is generating only ASCII, since the language-ecmascript's lexer
-- is currently (October 2016) still a bit naïve
arbitrary = fmap (ASCII . T.pack) $ listOf $ choose (minBound, '\127')
shrink xs = (ASCII . T.pack) <$> shrink (T.unpack $ getASCII xs)
-- Our arbitrary instance is generating only ASCII, since the language-ecmascript's lexer
-- is currently (October 2016) still a bit naïve
arbitrary = fmap (ASCII . T.pack) $ listOf $ choose (minBound, '\127')
shrink xs = (ASCII . T.pack) <$> shrink (T.unpack $ getASCII xs)


internalSpec :: Spec
internalSpec = describe "Internal" $ do
it "should generate only valid javascript identifiers for any ASCII route" $ do
let parseIdentifier = fmap T.pack. parse identifier ""
property $ \x -> let valid = toValidFunctionName $ getASCII x in
Right valid == parseIdentifier valid

it "should generate a valid javascript identifier when supplied with hyphens, unicode whitespace, non-bmp unicode" $ do
toValidFunctionName "a_--a\66352b\6158c\65075" `shouldBe` "a_abc\65075"

generatePYSpec :: TestNames -> (PyRequest -> Text) -> Spec
generatePYSpec n gen = describe specLabel $ do
let parseFromText = parse program ""
it "should generate valid javascript" $ do
let s = pyForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
parseFromText s `shouldSatisfy` isRight

it "should use non-empty function names" $ do
let (_ :<|> topLevel) = javascript (Proxy :: Proxy TopLevelRawAPI)
output $ genJS (topLevel "GET")
parseFromText (genJS $ topLevel "GET") `shouldSatisfy` isRight

it "should handle simple HTTP headers" $ do
let jsText = genJS $ javascript headerHandlingProxy
output jsText
parseFromText jsText `shouldSatisfy` isRight
jsText `shouldContain` "headerFoo"
jsText `shouldContain` (header n "Foo" $ "headerFoo")

it "should handle complex HTTP headers" $ do
let jsText = genJS $ javascript customAuthProxy
output jsText
parseFromText jsText `shouldSatisfy` isRight
jsText `shouldContain` "headerAuthorization"
jsText `shouldContain` (header n "Authorization" $ "\"Basic \" + headerAuthorization")

it "should handle complex, custom HTTP headers" $ do
let jsText = genJS $ javascript customHeaderProxy
output jsText
parseFromText jsText `shouldSatisfy` isRight
jsText `shouldContain` "headerXThreeMilePilot"
jsText `shouldContain` (header n "X-ThreeMilePilot" $ "\"I am good friends with \" + headerXThreeMilePilot")

it "should handle complex, custom HTTP headers (template replacement)" $ do
let jsText = genJS $ javascript customHeaderProxy2
output jsText
parseFromText jsText `shouldSatisfy` isRight
jsText `shouldContain` "headerXKlikatatIkatowi"
jsText `shouldContain` (header n "X-KlikatatIkatowi" $ "\"I would like to hear Swing Kids, Stick Figure Caraousel, \" + headerXKlikatatIkatowi + \" .\"")

it "can generate the whole javascript code string at once with pyForAPI" $ do
let jsStr = pyForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
parseFromText jsStr `shouldSatisfy` isRight
where
specLabel = "generateJS(" <> (show n) <> ")"
output _ = return ()
genJS req = gen req
header :: TestNames -> Text -> Text -> Text
header v headerName headerValue
| v `elem` [Requests, RequestsWith] = "xhr.setRequestHeader(\"" <> headerName <> "\", " <> headerValue <> ");\n"
| otherwise = "headers: { \"" <> headerName <> "\": " <> headerValue <> " }\n"
it "should only indent using whitespace" $
property $ \n -> indenter n indent == mconcat (replicate n (T.pack " "))

0 comments on commit 43757f5

Please sign in to comment.