-
Notifications
You must be signed in to change notification settings - Fork 16
/
UseCasePureSpec.hs
107 lines (88 loc) · 4.11 KB
/
UseCasePureSpec.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
module UseCasePureSpec where
import Data.Function ((&))
import qualified Data.Map.Strict as M
import Data.Time.Calendar
import Domain.ReservationDomain
import InterfaceAdapters.KVSInMemory
import Numeric.Natural
import Polysemy
import Polysemy.Error
import Polysemy.State
import Polysemy.Trace (Trace, ignoreTrace)
import Test.Hspec
import qualified UseCases.ReservationUseCase as UC
main :: IO ()
main = hspec spec
-- | Takes a program with effects and handles each effect till it gets reduced to Either ReservationError (ReservationMap‚ a). No IO !
runPure :: ReservationMap
-> Sem '[UC.Persistence, State ReservationMap, Error UC.ReservationError, Trace] a
-> Either UC.ReservationError (ReservationMap, a)
runPure kvsMap program =
program
& runKvsPure kvsMap -- run the key-value store on a simple ReservationMap
& runError @UC.ReservationError -- run error handling to produce an Either UC.ReservationError (ReservationMap, a)
& ignoreTrace -- run Trace by simply ignoring all messages
& run -- run a 'Sem' containing no effects as a pure value
-- Helper functions for interpreting all effects in a pure way. That is no IO !
runAvailableSeats :: ReservationMap -> Day -> Natural
runAvailableSeats kvsMap day = do
case runPure kvsMap (UC.availableSeats day) of
Right (_, numSeats) -> numSeats
Left err -> error "availableSeats failed"
runTryReservation :: ReservationMap -> Reservation -> Maybe ReservationMap
runTryReservation kvsMap res = do
case runPure kvsMap (UC.tryReservation res) of
Right (m, ()) -> Just m
Left err -> Nothing
runFetch :: ReservationMap -> Day -> [Reservation]
runFetch kvsMap day = do
case runPure kvsMap (UC.fetch day) of
Right (_, reservations) -> reservations
Left err -> error "fetch failed"
runListAll :: ReservationMap -> ReservationMap
runListAll kvsMap = do
case runPure kvsMap (UC.listAll) of
Right (_, m) -> m
Left err -> error "listALl failed"
runCancel :: ReservationMap -> Reservation -> Maybe ReservationMap
runCancel kvsMap res = do
case runPure kvsMap (UC.cancel res) of
Right (m, ()) -> Just m
Left err -> Nothing
-- setting up test fixtures
initReservations :: ReservationMap
initReservations = M.singleton day res
day = read "2020-05-02"
res = [Reservation day "Andrew M. Jones" "[email protected]" 4]
spec :: Spec
spec =
describe "Reservation Use Case (only pure code)" $ do
it "computes the number of available seats for a given day" $ do
(runAvailableSeats initReservations day) `shouldBe` 16
it "fetches a list of reservations from the KV store" $ do
(runFetch initReservations day) `shouldBe` res
it "returns Nothing if there are no reservations for a given day" $ do
let kvsMap = M.fromList []
(runFetch kvsMap day) `shouldBe` []
it "can retrieve a map of all reservations" $ do
let m = runListAll initReservations
M.size m `shouldBe` 1
it "can add a reservation if there are enough free seats" $ do
let goodReservation = Reservation day "Gabriella. Miller" "[email protected]" 4
let m = runTryReservation initReservations goodReservation
reservations = case m of
Just map -> runFetch map day
Nothing -> []
goodReservation `elem` reservations `shouldBe` True
it "reports an error if a reservation is not possible" $ do
let badReservation = Reservation day "Gabriella. Miller" "[email protected]" 17
(runTryReservation initReservations badReservation) `shouldBe` Nothing
it "cancels a reservation by deleting it from the KV store" $ do
let res1 = res !! 0
res2 = Reservation day "Gabriella. Miller" "[email protected]" 5
kvsMap = M.fromList [(day, [res1, res2])]
m = runCancel kvsMap res1
reservations = case m of
Just map -> runFetch map day
Nothing -> []
reservations `shouldBe` [res2]