packages feed

hup-0.3.0.2: src-hspec-test/Distribution/Hup/UploadSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Distribution.Hup.UploadSpec where

import Control.Exception                      (throwIO)
import Control.Monad
import Control.Monad.IO.Class                 (liftIO, MonadIO)
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LBS   --   pack
import Data.Maybe                             (fromJust)
import Data.Monoid                            ( (<>) )
import Network.HTTP.Client.MultipartFormData  (renderParts,webkitBoundary)
import Network.HTTP.Types as T                (statusCode,methodPost)
import Network.Wai.Test                       (simpleStatus,SResponse
                                              ,simpleBody)
import Test.Hspec
import qualified Test.Hspec.Wai as HWai       --(put, request)
import Test.Hspec.Wai.Internal                --(WaiSession,runWaiSession)
import Test.QuickCheck                        --(forAll)
import Test.QuickCheck.Monadic                --(assert, run, monadicIO )


import Distribution.Hup.Parse
import Distribution.Hup.Parse.Test
import Distribution.Hup.Upload
import Distribution.Hup.Upload.MockWebApp (webApp)

import qualified Distribution.Hup.WebTest

type ParsedTgz = Either String (IsDocumentation, Package)


-- `main` is here so that this module can be run from GHCi on its own.  It is
-- not needed for automatic spec discovery.
main :: IO ()
main =
  hspec spec

spec :: Spec
spec = do
  describe "testing with mocked requests" $
    describe "mocked requests" $
      context "when processed by a mock hackage server" $
        it "should go to the right web app path"
          httpMetadataRoundtripsOK'
  describe "testing with live HTTP requests" $
    -- this will be replaced with a stub unless the WEB_TESTS macro
    -- is defined.
    --
    -- 'webApp' is a very simple web application
    -- intended to mock some of the behaviour of a
    -- hackage server.
    Distribution.Hup.WebTest.liveTest webApp


httpMetadataRoundtripsOK' :: Property
httpMetadataRoundtripsOK' =
  forAll arbUpload $ \upl -> httpMetadataRoundtripsOK upl

httpMetadataRoundtripsOK :: Upload -> Property
httpMetadataRoundtripsOK upl = monadicIO $ do
  upl <- return $ upl { fileConts = Just "" }
  testRequest <- run $ buildTestRequest "" upl
  testResponse <- run $ sendTestRequest testRequest

  let resStatus = T.statusCode $ simpleStatus testResponse
      resBody :: String
      resBody =   LBS.unpack $ simpleBody testResponse
      _unserialized :: (IsDocumentation, IsCandidate, ParsedTgz)
      _unserialized@(recdIsDoc1, recdIsCand, parsedTgz) = read resBody

  let sentIsCand = isCandidate upl
      sentIsDoc  = uploadType  upl
      sentPkg    = package     upl

  assert (resStatus == 200)
  assert (sentIsCand == recdIsCand)
  assert (sentIsDoc  == recdIsDoc1)
  assert (parsedTgz == Right (sentIsDoc, sentPkg) )


sendTestRequest :: WaiSession SResponse -> IO SResponse
sendTestRequest testReq =
  runWaiSession testReq webApp


testPut :: String -> LBS.ByteString -> WaiSession SResponse
testPut url conts =
  HWai.put (BS.pack url) conts

testPost
  :: String -> FilePath -> LBS.ByteString -> IO (WaiSession SResponse)
testPost url fileName fileConts = do
  boundary <- webkitBoundary
  let part    = mkPart fileName fileConts
      headers = [("Content-Type",
                    "multipart/form-data; boundary=" <> boundary)]
  body <- bodyToByteString <$> renderParts boundary [part]
  return $ HWai.request T.methodPost (BS.pack url) headers body


-- Only call when fileConts has something in it.
buildTestRequest
  :: String -> Upload -> IO (WaiSession SResponse)
buildTestRequest serverUrl upl  = do
  let (Upload _ filePath fileConts uploadType _pkgType ) = upl
      url = getUploadUrl serverUrl upl
  fileConts <- return (fromJust fileConts)
  case uploadType of
      IsPackage ->
         testPost url filePath fileConts
      IsDocumentation ->
         return $ testPut url fileConts

ioAssert :: MonadIO f => Bool -> String -> f ()
ioAssert pred mesg =
  unless pred $
        liftIO $ throwIO $ userError mesg