content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE CPP #-}
  module Test.Hspec.Wai.UtilSpec (main, spec) where
  
  import           Test.Hspec
  import           Test.Hspec.QuickCheck
  import           Test.QuickCheck
  import           Test.Hspec.Wai.Util
  import           Network.HTTP.Types (parseSimpleQuery)
  import           Data.ByteString (ByteString)
  import qualified Data.ByteString.Lazy as LB
  import qualified Data.Text as T
  import qualified Data.Text.Encoding as T
  
  #if !MIN_VERSION_base(4,11,0)
  import           Data.Monoid
  #endif
  
  main :: IO ()
  main = hspec spec
  
  decodePair :: (ByteString, ByteString) -> (String, String)
  decodePair (a, b) = (decode a, decode b)
    where
      decode :: ByteString -> String
      decode = newlineNormalize . T.unpack . T.decodeUtf8
  
      newlineNormalize :: String -> String
      newlineNormalize input = case input of
        [] -> []
        '\r' : '\n' : xs -> '\n': newlineNormalize xs
        x : xs -> x : newlineNormalize xs
  
  spec :: Spec
  spec = do
    describe "formUrlEncodeQuery" $ do
      it "separates keys from values by =" $ do
        formUrlEncodeQuery [("foo", "bar")] `shouldBe` "foo=bar"
  
      it "separates pairs by &" $ do
        formUrlEncodeQuery [("foo", "bar"), ("foo", "baz")] `shouldBe` "foo=bar&foo=baz"
  
      it "applies newline normalization" $ do
        formUrlEncodeQuery [("text", "foo\nbar\nbaz\n")] `shouldBe` "text=foo%0D%0Abar%0D%0Abaz%0D%0A"
  
      it "handles Unicode characters" $ do
        formUrlEncodeQuery [("foo","bar-\955-baz")] `shouldBe` "foo=bar-%CE%BB-baz"
  
      context "when encoding characters in the printable ASCII range" $ do
        let input = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
            reference = "+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E"
  
        it "behaves like Firefox" $ do
          formUrlEncodeQuery [("foo", input)] `shouldBe` "foo=" <> reference
  
      modifyMaxSize (`div` 5) $ do
        it "can be reverted by Network.HTTP.Types.parseSimpleQuery" $ do
          property $ \xs -> do
            (map decodePair . parseSimpleQuery . LB.toStrict . formUrlEncodeQuery) xs `shouldBe` xs
  
    describe "safeToString" $ do
      context "when used on an empty string" $ do
        it "returns Nothing" $ do
          safeToString "" `shouldBe` Nothing
  
    describe "formatHeader" $ do
      it "formats header" $ do
        let header = ("Content-Type", "application/json")
        formatHeader header `shouldBe` "  Content-Type: application/json"
  
      describe "when ends with whitespace" $ do
        it "uses show" $ do
          let header = ("Content-Type", "application/json  ")
          formatHeader header `shouldBe` "  " ++ show header
  
      describe "when contains non-print characters" $ do
        it "uses show" $ do
          let header = ("Content-\nType", "application/json")
          formatHeader header `shouldBe` "  " ++ show header
  
      describe "when header is not decodable as UTF-8" $ do
        it "uses show" $ do
          let header = ("Content-Type", "\xc3\x28")
          formatHeader header `shouldBe` "  " ++ show header
  