content: {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE LambdaCase #-}
  {-# OPTIONS_GHC -fno-warn-deprecations -fno-warn-unused-matches -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-orphans #-}
  
  import qualified Data.Aeson as A
  import qualified Data.ByteString.Lazy.Char8 as BCL
  import qualified Data.Text as T
  import qualified Data.Time as TI
  import qualified Lens.Micro as L
  import qualified Network.HTTP.Client as NH
  import qualified Network.HTTP.Types.Status as NH
  
  import Data.Typeable (Proxy(..))
  import Test.Hspec
  import Test.Hspec.QuickCheck
  import Test.HUnit
  import Test.HUnit.Lang
  
  import Control.Monad.IO.Class
  import Data.IORef
  import qualified Data.Map.Strict as Map
  import Data.Map.Strict (Map)
  import System.Environment (getEnvironment)
  
  import qualified OpenAPIPetstore as S
  
  import Data.Monoid ((<>))
  
  -- * UTILS
  
  assertSuccess :: Expectation
  assertSuccess = Success `shouldBe` Success
  
  -- * MAIN
  
  main :: IO ()
  main = do
  
    env <- getEnvironment
  
    mgr <- NH.newManager NH.defaultManagerSettings
  
    config0 <- S.withStdoutLogging =<< S.newConfig 
  
    let config =
          -- configure host
          case lookup "HOST" env of
              Just h -> config0 { S.configHost = BCL.pack h }
              _ -> config0 { S.configHost = "http://0.0.0.0/v2" }
          -- each configured auth method is only applied to requests that specify them
          `S.addAuthMethod` S.AuthBasicHttpBasicTest "username" "password"
          `S.addAuthMethod` S.AuthApiKeyApiKey "secret-key"
          `S.addAuthMethod` S.AuthApiKeyApiKeyQuery "secret-key"
          `S.addAuthMethod` S.AuthOAuthPetstoreAuth "secret-key"
  
    putStrLn "\n******** CONFIG ********"
    putStrLn (show config)
  
  
    hspec $ do
      testPetOps mgr config
      testStoreOps mgr config
      testUserOps mgr config
  
  -- * PET TESTS
  
  testPetOps :: NH.Manager -> S.OpenAPIPetstoreConfig -> Spec
  testPetOps mgr config = 
  
    describe "** pet operations" $ do
  
      _pet <- runIO $ newIORef (Nothing :: Maybe S.Pet)
  
      it "addPet" $ do
        let addPetRequest =
               S.addPet (S.ContentType S.MimeJSON) (S.mkPet "name" ["url1", "url2"])
        addPetResponse <- S.dispatchLbs mgr config addPetRequest
        NH.responseStatus addPetResponse `shouldBe` NH.status200
        case A.eitherDecode (NH.responseBody addPetResponse) of
          Right pet -> do
            _pet `writeIORef` Just pet 
            assertSuccess
          Left e -> assertFailure e
  
      around (\go ->
        readIORef _pet >>= \case
          Just pet@S.Pet {S.petId = Just petId} -> go (petId, pet)
          _ -> pendingWith "no petId") $
        it "getPetById" $ \(petId, pet) -> do
          let getPetByIdRequest = S.getPetById (S.Accept S.MimeJSON) (S.PetId petId)
          getPetByIdRequestResult <- S.dispatchMime mgr config getPetByIdRequest 
          NH.responseStatus (S.mimeResultResponse getPetByIdRequestResult) `shouldBe` NH.status200
          case S.mimeResult getPetByIdRequestResult of
            Right p -> p `shouldBe` pet
            Left (S.MimeError e _) -> assertFailure e
  
      it "findPetsByStatus" $ do
        let findPetsByStatusRequest = S.findPetsByStatus (S.Accept S.MimeJSON)
                                          (S.Status [ S.E'Status2'Available
                                                    , S.E'Status2'Pending
                                                    , S.E'Status2'Sold])
        findPetsByStatusResult <- S.dispatchMime mgr config findPetsByStatusRequest
        NH.responseStatus (S.mimeResultResponse findPetsByStatusResult) `shouldBe` NH.status200
        case S.mimeResult findPetsByStatusResult of
          Right r -> length r `shouldSatisfy` (> 0)
          Left (S.MimeError e _) -> assertFailure e
        
      it "findPetsByTags" $ do
        let findPetsByTagsRequest = S.findPetsByTags (S.Accept S.MimeJSON) (S.Tags ["name","tag1"])
        findPetsByTagsResult <- S.dispatchMime mgr config findPetsByTagsRequest
        NH.responseStatus (S.mimeResultResponse findPetsByTagsResult) `shouldBe` NH.status200
        case S.mimeResult findPetsByTagsResult of
          Right r -> length r `shouldSatisfy` (> 0)
          Left (S.MimeError e _) -> assertFailure e
  
      around (\go ->
        readIORef _pet >>= \case
          Just pet -> go pet
          _ -> pendingWith "no pet") $
        it "updatePet" $ \pet -> do
          let updatePetRequest = S.updatePet (S.ContentType S.MimeJSON)
                (pet
                   { S.petStatus   = Just S.E'Status2'Available
                   , S.petCategory = Just (S.Category (Just 3) "catname")
                   })
          updatePetResponse <- S.dispatchLbs mgr config updatePetRequest 
          NH.responseStatus updatePetResponse `shouldBe` NH.status200
  
      it "updatePetWithFormRequest" $ do
        readIORef _pet >>= \case
          Just S.Pet {S.petId = Just petId} -> do
            let updatePetWithFormRequest = S.updatePetWithForm
                  (S.PetId petId)
                  `S.applyOptionalParam` S.Name2 "petName"
                  `S.applyOptionalParam` S.StatusText "pending"
            updatePetWithFormResponse <- S.dispatchLbs mgr config updatePetWithFormRequest 
            NH.responseStatus updatePetWithFormResponse `shouldBe` NH.status200
          _ -> pendingWith "no pet"
  
      around (\go ->
        readIORef _pet >>= \case
          Just pet@S.Pet {S.petId = Just petId} -> go petId
          _ -> pendingWith "no petId") $
        it "uploadFile" $ \petId -> do
            let uploadFileRequest = S.uploadFile (S.PetId petId)
                    `S.applyOptionalParam` S.File2 "package.yaml"
                    `S.applyOptionalParam` S.AdditionalMetadata "a package.yaml file"
            uploadFileRequestResult <- S.dispatchMime mgr config uploadFileRequest 
            NH.responseStatus (S.mimeResultResponse uploadFileRequestResult) `shouldBe` NH.status200
            case S.mimeResult uploadFileRequestResult of
                Right _ -> assertSuccess
                Left (S.MimeError e _) -> assertFailure e
  
      around (\go ->
        readIORef _pet >>= \case
          Just pet@S.Pet {S.petId = Just petId} -> go petId
          _ -> pendingWith "no petId") $
        it "deletePet" $ \petId -> do
          let deletePetRequest = S.deletePet (S.PetId petId)
                    `S.applyOptionalParam` S.ApiKey "api key"
          deletePetResponse <- S.dispatchLbs mgr config deletePetRequest
          NH.responseStatus deletePetResponse `shouldBe` NH.status200
  
  -- * STORE TESTS
    
  testStoreOps :: NH.Manager -> S.OpenAPIPetstoreConfig -> Spec
  testStoreOps mgr config = do
  
    describe "** store operations" $ do
  
      _order <- runIO $ newIORef (Nothing :: Maybe S.Order)
  
      it "getInventory" $ do
        let getInventoryRequest = S.getInventory
              `S.setHeader` [("api_key","special-key")] 
        getInventoryRequestRequestResult <- S.dispatchMime mgr config getInventoryRequest 
        NH.responseStatus (S.mimeResultResponse getInventoryRequestRequestResult) `shouldBe` NH.status200
        case S.mimeResult getInventoryRequestRequestResult of
            Right r -> length r `shouldSatisfy` (> 0)
            Left (S.MimeError e _) -> assertFailure e
  
      it "placeOrder" $ do
        now <- TI.getCurrentTime
        let placeOrderRequest = S.placeOrder (S.ContentType S.MimeJSON) (S.Accept S.MimeJSON)
              (S.mkOrder
               { S.orderId = Just 21
               , S.orderQuantity = Just 210
               , S.orderShipDate = Just (S.DateTime now)
               })
        placeOrderResult <- S.dispatchMime mgr config placeOrderRequest 
        NH.responseStatus (S.mimeResultResponse placeOrderResult) `shouldBe` NH.status200
        case S.mimeResult placeOrderResult of
            Right order -> do
              _order `writeIORef` Just order 
              assertSuccess
            Left (S.MimeError e _) -> assertFailure e
  
      around (\go ->
        readIORef _order >>= \case
          Just order@S.Order {S.orderId = Just orderId} -> go (orderId, order)
          _ -> pendingWith "no orderId") $
        it "getOrderById" $ \(orderId, order) -> do
          let getOrderByIdRequest = S.getOrderById  (S.Accept S.MimeJSON) (S.OrderId orderId)
          getOrderByIdRequestResult <- S.dispatchMime mgr config getOrderByIdRequest
          NH.responseStatus (S.mimeResultResponse getOrderByIdRequestResult) `shouldBe` NH.status200
          case S.mimeResult getOrderByIdRequestResult of
              Right o -> o `shouldBe` order
              Left (S.MimeError e _) -> assertFailure e
  
      around (\go ->
        readIORef _order >>= \case
          Just S.Order {S.orderId = Just orderId} -> go (T.pack (show orderId))
          _ -> pendingWith "no orderId") $
        it "deleteOrder" $ \orderId -> do
          let deleteOrderRequest = S.deleteOrder (S.OrderIdText orderId)
          deleteOrderResult <- S.dispatchLbs mgr config deleteOrderRequest 
          NH.responseStatus deleteOrderResult `shouldBe` NH.status200
  
  
  -- * USER TESTS
  
  testUserOps :: NH.Manager -> S.OpenAPIPetstoreConfig -> Spec
  testUserOps mgr config = do
  
    describe "** user operations" $ do
  
      let _username = "hsusername"
          _password = "password1"
          _user =
            S.mkUser
            { S.userId = Just 21
            , S.userUsername = Just _username
            , S.userEmail = Just "xyz@example.com"
            , S.userUserStatus = Just 0
            }
          _users =
            take 8 $
            drop 1 $
            iterate
              (L.over (S.userUsernameL . L._Just) (<> "*") .
               L.over (S.userIdL . L._Just) (+ 1))
              _user
  
      before (pure _user) $
        it "createUser" $ \user -> do
          let createUserRequest = S.createUser (S.ContentType S.MimeJSON) user
          createUserResult <- S.dispatchLbs mgr config createUserRequest 
          NH.responseStatus createUserResult `shouldBe` NH.status200
  
      before (pure _users) $
        it "createUsersWithArrayInput" $ \users -> do
          let createUsersWithArrayInputRequest = S.createUsersWithArrayInput (S.ContentType S.MimeJSON) (S.Body users)
          createUsersWithArrayInputResult <- S.dispatchLbs mgr config createUsersWithArrayInputRequest
          NH.responseStatus createUsersWithArrayInputResult `shouldBe` NH.status200
  
      before (pure _users) $
        it "createUsersWithListInput" $ \users -> do
          let createUsersWithListInputRequest = S.createUsersWithListInput (S.ContentType S.MimeJSON) (S.Body users)
          createUsersWithListInputResult <- S.dispatchLbs mgr config createUsersWithListInputRequest 
          NH.responseStatus createUsersWithListInputResult `shouldBe` NH.status200
  
      before (pure (_username, _user)) $
        it "getUserByName" $ \(username, user) -> do
          let getUserByNameRequest = S.getUserByName (S.Accept S.MimeJSON) (S.Username username)
          getUserByNameResult <- S.dispatchMime mgr config getUserByNameRequest 
          NH.responseStatus (S.mimeResultResponse getUserByNameResult) `shouldBe` NH.status200
          case S.mimeResult getUserByNameResult of
            Right u -> u `shouldBe` user
            Left (S.MimeError e _) -> assertFailure e
  
      before (pure (_username, _password)) $
        it "loginUser" $ \(username, password) -> do
          let loginUserRequest = S.loginUser (S.Accept S.MimeJSON) (S.Username username) (S.Password password)
          loginUserResult <- S.dispatchLbs mgr config loginUserRequest
          NH.responseStatus loginUserResult `shouldBe` NH.status200
  
      before (pure (_username, _user)) $
        it "updateUser" $ \(username, user) -> do
          let updateUserRequest = S.updateUser (S.ContentType S.MimeJSON) user (S.Username username) 
          updateUserResult <- S.dispatchLbs mgr config updateUserRequest
          NH.responseStatus updateUserResult `shouldBe` NH.status200
  
      it "logoutuser" $ do
          logoutUserResult <- S.dispatchLbs mgr config S.logoutUser
          NH.responseStatus logoutUserResult `shouldBe` NH.status200
  
      before (pure _username) $
        it "deleteUser" $ \username -> do
          let deleteUserRequest = S.deleteUser (S.Username username)
          deleteUserResult <- S.dispatchLbs mgr config deleteUserRequest 
          NH.responseStatus deleteUserResult `shouldBe` NH.status200
  