packages feed

paypal-adaptive-hoops-0.11.0.0: tests/Remote.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}

module Main where

import           Control.Applicative
import           Control.Monad
import           Data.Aeson
import qualified Data.ByteString.Lazy           as B
import           Data.HashMap.Strict            as H
import           Data.Text                      (Text)
import           PayPal.Adaptive
import qualified PayPal.Adaptive.Deposit        as DP
import           PayPal.Adaptive.Internal
import qualified PayPal.Adaptive.Lookup         as LP
import qualified PayPal.Adaptive.Withdrawal     as WD
import           Test.Framework                 (Test, defaultMain, testGroup)
import           Test.Framework.Providers.HUnit (testCase)
import           Test.HUnit                     hiding (Test)
import           Utils

main :: IO ()
main = defaultMain [integrationRemote]

integrationRemote :: Test
integrationRemote = testGroup "remote"
  [ testCase
      "withdrawal succeeds with good arguments"
      correctWithdrawal
  , testCase
      "withdrawal with a bad account address gives the correct error"
      withdrawalAccountAddressDNE
  , testCase
      "withdrawal with a bad user address gives the correct error"
      withdrawalUserAddressDNE
  , testCase
      "withdrawal without a large enough balance gives the correct error"
      withdrawalNotEnoughFunds
  , testCase
      "deposit succeeds with good arguments"
      correctDepositBeginning
  , testCase
      "deposit with a bad account address does NOT produce an error"
      depositAccountAddressDNE
  , testCase
      "deposit with a bad user address gives the correct error"
      depositUserAddressDNE
  ]

-- | Currently the ID for all sandbox apps.
appId :: Text
appId = "APP-80W284485P519543T"

instance FromJSON Client where
  parseJSON = withObject "client credentials" $ \o ->
    Client appId                 <$>
    o .: "sandboxClientUid"      <*>
    pure Sandbox                 <*>
    o .: "sandboxClientPassword" <*>
    o .: "sandboxClientSig"      <*>
    o .: "sandboxAccountEmail"

config :: IO (Client, Text, Text)
config = do
  b <- B.readFile "sandbox.json"
  client <- assertRight (eitherDecode b)
  Object o <- assertRight (eitherDecode b)
  String userEmail <- assertJust "sandboxUserEmail not found"
    $ H.lookup "sandboxUserEmail" o
  String notSandboxEmail <- assertJust "randomNonSandboxUserEmail not found"
    $ H.lookup "randomNonSandboxUserEmail" o
  return (client, userEmail, notSandboxEmail)

incorrectEmailClient :: Text -> Client -> Client
incorrectEmailClient badAccountEmail (Client a b c d e _) = Client a b c d e badAccountEmail

withdrawal :: Text -> WD.Withdrawal
withdrawal userAddr = WD.Withdrawal
  { WD._amount        = USD 1
  , WD._receiverEmail = userAddr
  }

deposit :: Text -> DP.Deposit
deposit userAddr = DP.Deposit
  { DP._amount      = USD 1
  , DP._senderEmail = userAddr
  , DP._returnUrl   = "https://example.com/"
  , DP._cancelUrl   = "https://example.com/cancel"
  }


correctWithdrawal :: Assertion
correctWithdrawal = do
  (client, userEmail, _) <- config
  wdResp <- assertRight =<< WD.req client (withdrawal userEmail)
  lpResp <- assertRight =<< LP.req client (LP.LookupPayKey $ _crPayKey wdResp)
  void $ assertRight (checkComplete lpResp)

withdrawalAccountAddressDNE :: Assertion
withdrawalAccountAddressDNE = do
  (client, userEmail, notSandboxEmail) <- config
  let c = incorrectEmailClient notSandboxEmail client
  adaptiveErr <- assertLeft =<< WD.req c (withdrawal userEmail)
  assertEqual "send error due to a nonexistent account address" AeNoSuchEmail adaptiveErr

withdrawalUserAddressDNE :: Assertion
withdrawalUserAddressDNE = do
  (client, _, notSandboxEmail) <- config
  payResp <- assertExpected =<< assertLeft =<< WD.req client (withdrawal notSandboxEmail)
  assertLookup =<< assertRight =<< LP.req client (LP.LookupPayKey $ _prPayKey payResp)
  where
    assertExpected :: AdaptiveErr -> IO PayResp
    assertExpected (AePending payResp) = do
      assertEqual
        "withdraw to nonexistent user addresses return no PayInfos"
        0 (length $ _prPayInfos payResp)
      return payResp
    assertExpected _ =
      assertFalse "send error due to a nonexistent user address didn't return AePending"
      >> fail "checkResp failed"

    assertLookup :: PayResp -> Assertion
    assertLookup a = do
      info <- assertRight $ do
        payExecStatusCompleted a
        errorFieldNothing a
        checkPayKey a
        getPayInfo a
      assertEqual "pending withdrawal transaction status" Nothing (_piTransactionStatus info)

withdrawalNotEnoughFunds :: Assertion
withdrawalNotEnoughFunds = do
  (client, userEmail, _) <- config
  adaptiveErr <- assertLeft =<< WD.req client (largeWithdrawal userEmail)
  assertEqual "send error due to not enough funds" (AeErrCodes [520009]) adaptiveErr
  where
    largeWithdrawal :: Text -> WD.Withdrawal
    largeWithdrawal userAddr = WD.Withdrawal
      { WD._amount        = USD 100000000
      , WD._receiverEmail = userAddr
      }

correctDepositBeginning :: Assertion
correctDepositBeginning = do
  (client, userEmail, _) <- config
  payKey <- assertRight =<< DP.req client (deposit userEmail)
  assertExpected =<< assertRight =<< LP.req client (LP.LookupPayKey payKey)
  where
    assertExpected :: PayResp -> Assertion
    assertExpected a = do
      info <- assertRight $ do
        payExecStatusCreated a
        errorFieldNothing a
        checkPayKey a
        getPayInfo a
      assertEqual "pending deposit transaction status" Nothing (_piTransactionStatus info)

depositAccountAddressDNE :: Assertion
depositAccountAddressDNE = do
  (client, userEmail, notSandboxEmail) <- config
  let c = incorrectEmailClient notSandboxEmail client
  payKey <- assertRight =<< DP.req c (deposit userEmail)
  assertExpected =<< assertRight =<< LP.req client (LP.LookupPayKey payKey)
  where
    assertExpected :: PayResp -> Assertion
    assertExpected a = do
      info <- assertRight $ do
        payExecStatusCreated a
        errorFieldNothing a
        checkPayKey a
        getPayInfo a
      assertEqual "status for deposit where to address isn't an account" Nothing (_piTransactionStatus info)

depositUserAddressDNE :: Assertion
depositUserAddressDNE = do
  (client, _, notSandboxEmail) <- config
  adaptiveErr <- assertLeft =<< DP.req client (deposit notSandboxEmail)
  assertEqual "create error due to a nonexistent user address" AeNoSuchEmail adaptiveErr