{-# LANGUAGE OverloadedStrings, CPP #-}

module HPACK.HuffmanSpec where

#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<$>))
#endif
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.Char (toLower)
import Data.Hex
import Data.Maybe (fromJust)
import Network.HPACK
import Network.HPACK.Huffman
import Test.Hspec
import Test.Hspec.QuickCheck

testData :: [(ByteString, ByteString)]
testData = [
    ("", "")
  , ("www.example.com", "f1e3c2e5f23a6ba0ab90f4ff")
  , ("no-cache", "a8eb10649cbf")
  , ("custom-key", "25a849e95ba97d7f")
  , ("custom-value", "25a849e95bb8e8b4bf")
  , ("private", "aec3771a4b")
  , ("Mon, 21 Oct 2013 20:13:21 GMT", "d07abe941054d444a8200595040b8166e082a62d1bff")
  , ("https://www.example.com", "9d29ad171863c78f0b97c8e9ae82ae43d3")
  , ("Mon, 21 Oct 2013 20:13:22 GMT", "d07abe941054d444a8200595040b8166e084a62d1bff")
  , ("gzip", "9bd9ab")
  , ("foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", "94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007")
  ]

shouldBeEncoded :: ByteString -> ByteString -> Expectation
shouldBeEncoded inp out = do
    out' <- BS.map toLower . hex <$> encodeHuffman inp
    out' `shouldBe` out

shouldBeDecoded :: ByteString -> ByteString -> Expectation
shouldBeDecoded inp out = do
    out' <- decodeHuffman $ fromJust $ unhex inp
    out' `shouldBe` out

tryDecode :: ByteString -> IO ByteString
tryDecode inp = decodeHuffman $ fromJust $ unhex inp

spec :: Spec
spec = do
    describe "encode and decode" $ do
        prop "duality" $ \cs -> do
            let bs = BS.pack cs
            es <- encodeHuffman bs
            ds <- decodeHuffman es
            ds `shouldBe` bs
    describe "encode" $ do
        it "encodes" $ do
            mapM_ (\(x,y) -> x `shouldBeEncoded` y) testData
    describe "decode" $ do
        it "decodes" $ do
            tryDecode       "ff" `shouldThrow` (== TooLongEos)
            tryDecode "ffffeaff" `shouldThrow` (== TooLongEos)
            "ffffea" `shouldBeDecoded` "\9"
            mapM_ (\(x,y) -> y `shouldBeDecoded` x) testData
