From 25f24e4d7ec8dddf99e300fbe98038b1bfdc365d Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Thu, 29 Aug 2019 19:36:04 -0400 Subject: [PATCH] Make the typeclass test suite easily reusable --- PaymentServer.cabal | 1 + test/SpecPersistence.hs | 76 +++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/PaymentServer.cabal b/PaymentServer.cabal index 8ef0a3a..91290a9 100644 --- a/PaymentServer.cabal +++ b/PaymentServer.cabal @@ -50,6 +50,7 @@ test-suite PaymentServer-test , hspec , hspec-wai , hspec-wai-json + , hspec-expectations , aeson , stripe-core , time diff --git a/test/SpecPersistence.hs b/test/SpecPersistence.hs index 8d1c21c..3fdca1b 100644 --- a/test/SpecPersistence.hs +++ b/test/SpecPersistence.hs @@ -9,17 +9,31 @@ module SpecPersistence where import Test.QuickCheck ( Property - , (===) + , (==>) ) import Control.Monad.IO.Class ( liftIO ) +import Test.Hspec + ( Spec + , describe + , it + ) +import Test.Hspec.Expectations + ( shouldReturn + ) +import Test.QuickCheck + ( property + ) import Test.QuickCheck.Monadic ( monadicIO , run , assert , pre ) +import Test.QuickCheck.Instances.Text + ( + ) import PaymentServer.Persistence ( RedeemError(NotPaid, AlreadyRedeemed) , Voucher @@ -29,51 +43,45 @@ import PaymentServer.Persistence ) -- | A voucher which has not been paid for cannot be redeemed. -unpaidVoucherNotRedeemable :: VoucherDatabase d => IO d -> Voucher -> Fingerprint -> Property -unpaidVoucherNotRedeemable getDB voucher fingerprint = monadicIO $ do +unpaidVoucherNotRedeemable :: VoucherDatabase d => IO d -> Property +unpaidVoucherNotRedeemable getDB = property $ \voucher fingerprint -> do db <- liftIO getDB - result <- run $ redeemVoucher db voucher fingerprint - assert (result == Left NotPaid) - --- | The in-memory implementation for unpaidVoucherNotRedeemable. -prop_memory_unpaidVoucherNotRedeemable = unpaidVoucherNotRedeemable memory + redeemVoucher db voucher fingerprint `shouldReturn` Left NotPaid -- | A voucher which is paid for can be redeemed with any fingerprint. -paidVoucherRedeemable :: VoucherDatabase d => IO d -> Voucher -> Fingerprint -> Property -paidVoucherRedeemable getDB voucher fingerprint = monadicIO $ do +paidVoucherRedeemable :: VoucherDatabase d => IO d -> Property +paidVoucherRedeemable getDB = property $ \voucher fingerprint -> do db <- liftIO getDB - () <- run $ payForVoucher db voucher - result <- run $ redeemVoucher db voucher fingerprint - assert (result == Right ()) - --- | The in-memory implementation for paidVoucherRedeemable. -prop_memory_paidVoucherRedeemable = paidVoucherRedeemable memory + () <- payForVoucher db voucher + redeemVoucher db voucher fingerprint `shouldReturn` Right () -- | A voucher which is paid for can be redeemed more than once as long as the -- same fingerprint is used each time. -paidVoucherMultiRedeemable :: VoucherDatabase d => IO d -> Voucher -> Fingerprint -> Property -paidVoucherMultiRedeemable getDB voucher fingerprint = monadicIO $ do +paidVoucherMultiRedeemable :: VoucherDatabase d => IO d -> Property +paidVoucherMultiRedeemable getDB = property $ \voucher fingerprint -> do db <- liftIO getDB - () <- run $ payForVoucher db voucher + () <- payForVoucher db voucher let redeem = redeemVoucher db voucher fingerprint - run redeem - result <- run redeem - assert (result == Right ()) - --- | The in-memory implementation for paidVoucherMultiRedeemable. -prop_memory_paidVoucherMultiRedeemable = paidVoucherMultiRedeemable memory + redeem + redeem `shouldReturn` Right () -- | A voucher which is paid for can not be redeemed a second time with a -- different fingerprint than was used on the first attempt. -paidVoucherMismatchFingerprint :: VoucherDatabase d => IO d -> Voucher -> Fingerprint -> Fingerprint -> Property -paidVoucherMismatchFingerprint getDB voucher fingerprint fingerprint' = monadicIO $ do - pre (fingerprint /= fingerprint') +paidVoucherMismatchFingerprint :: VoucherDatabase d => IO d -> Property +paidVoucherMismatchFingerprint getDB = property $ \voucher fingerprint fingerprint' -> + fingerprint /= fingerprint' ==> do db <- liftIO getDB - () <- run $ payForVoucher db voucher + () <- payForVoucher db voucher let redeem = redeemVoucher db voucher - run $ redeem fingerprint - result <- run $ redeem fingerprint' - assert (result == Left AlreadyRedeemed) + redeem fingerprint + redeem fingerprint' `shouldReturn` Left AlreadyRedeemed + +makeSpec :: VoucherDatabase d => IO d -> Spec +makeSpec getDB = do + describe "voucher interactions" $ do + it "denies redemption of a not-paid-for voucher" $ unpaidVoucherNotRedeemable getDB + it "allows redemption of paid-for vouchers" $ paidVoucherRedeemable getDB + it "allows multiple redemption as long as the same fingerprint is used" $ paidVoucherMultiRedeemable getDB + it "denies a subsequent redemption with a different fingerprint" $ paidVoucherMismatchFingerprint getDB --- | The in-memory implementation for paidVoucherMismatchFingerprint. -prop_memory_paidVoucherMismatchFingerprint = paidVoucherMismatchFingerprint memory +spec_memory = makeSpec memory -- GitLab