@@ -0,0 +1,149 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Aff (launchAff_)
7+import Effect.Aff.Class (liftAff)
8+import Effect.Class (liftEffect)
9+import Effect.Ref as Ref
10+import Effect.Aff.Retry (RetryStatus(..), limitRetries, constantDelay)
11+import Data.Time.Duration (Milliseconds(..))
12+import Test.Spec (describe, it)
13+import Test.Spec.Assertions (shouldEqual)
14+import Test.Spec.Reporter (consoleReporter)
15+import Test.Spec.Runner (runSpec)
16+import Yoga.Om as Om
17+import Yoga.Om.Retry (recovering, repeating)
18+
19+main :: Effect Unit
20+main = launchAff_ $ runSpec [ consoleReporter ] do
21+ describe "Yoga.Om.Retry - recovering" do
22+
23+ it "retries on matching errors up to the limit" do
24+ attemptsRef <- Ref.new 0 # liftEffect
25+ recovering (constantDelay (Milliseconds 0.0) <> limitRetries 3)
26+ (\_ -> { networkError: \(_ :: String) -> pure true })
27+ (\_ -> do
28+ attempts <- Ref.modify (_ + 1) attemptsRef # liftEffect
29+ Om.throw { networkError: "fail " <> show attempts }
30+ )
31+ # Om.runOm {}
32+ { exception: \_ -> pure unit
33+ , networkError: \_ -> pure unit
34+ }
35+ # liftAff
36+ attempts <- Ref.read attemptsRef # liftEffect
37+ attempts `shouldEqual` 4
38+
39+ it "does not retry on non-matching errors" do
40+ attemptsRef <- Ref.new 0 # liftEffect
41+ recovering (constantDelay (Milliseconds 0.0) <> limitRetries 3)
42+ (\_ -> { networkError: \(_ :: String) -> pure true })
43+ (\_ -> do
44+ Ref.modify_ (_ + 1) attemptsRef # liftEffect
45+ Om.throw { authError: "unauthorized" }
46+ )
47+ # Om.runOm {}
48+ { exception: \_ -> pure unit
49+ , networkError: \_ -> pure unit
50+ , authError: \_ -> pure unit
51+ }
52+ # liftAff
53+ attempts <- Ref.read attemptsRef # liftEffect
54+ attempts `shouldEqual` 1
55+
56+ it "succeeds after transient failures" do
57+ attemptsRef <- Ref.new 0 # liftEffect
58+ result <-
59+ recovering (constantDelay (Milliseconds 0.0) <> limitRetries 5)
60+ (\_ -> { networkError: \(_ :: String) -> pure true })
61+ (\_ -> do
62+ attempts <- Ref.modify (_ + 1) attemptsRef # liftEffect
63+ when (attempts < 3) do
64+ Om.throw { networkError: "transient" }
65+ pure "success"
66+ )
67+ # Om.runOm {}
68+ { exception: \_ -> pure "failed"
69+ , networkError: \_ -> pure "failed"
70+ }
71+ # liftAff
72+ result `shouldEqual` "success"
73+ attempts <- Ref.read attemptsRef # liftEffect
74+ attempts `shouldEqual` 3
75+
76+ it "uses RetryStatus to decide whether to retry" do
77+ statusesRef <- Ref.new [] # liftEffect
78+ recovering (constantDelay (Milliseconds 0.0) <> limitRetries 3)
79+ (\(RetryStatus s) ->
80+ { networkError: \(_ :: Unit) -> do
81+ Ref.modify_ (_ <> [s.iterNumber]) statusesRef # liftEffect
82+ pure true
83+ })
84+ (\_ -> Om.throw { networkError: unit })
85+ # Om.runOm {}
86+ { exception: \_ -> pure unit
87+ , networkError: \_ -> pure unit
88+ }
89+ # liftAff
90+ statuses <- Ref.read statusesRef # liftEffect
91+ statuses `shouldEqual` [0, 1, 2, 3]
92+
93+ it "stops retrying when check returns false" do
94+ attemptsRef <- Ref.new 0 # liftEffect
95+ recovering (constantDelay (Milliseconds 0.0) <> limitRetries 10)
96+ (\(RetryStatus s) ->
97+ { networkError: \(_ :: Unit) -> pure (s.iterNumber < 2) })
98+ (\_ -> do
99+ Ref.modify_ (_ + 1) attemptsRef # liftEffect
100+ Om.throw { networkError: unit }
101+ )
102+ # Om.runOm {}
103+ { exception: \_ -> pure unit
104+ , networkError: \_ -> pure unit
105+ }
106+ # liftAff
107+ attempts <- Ref.read attemptsRef # liftEffect
108+ attempts `shouldEqual` 3
109+
110+ describe "Yoga.Om.Retry - repeating" do
111+
112+ it "repeatings while condition holds" do
113+ counterRef <- Ref.new 0 # liftEffect
114+ result <-
115+ repeating (constantDelay (Milliseconds 0.0) <> limitRetries 10)
116+ (\_ n -> pure (n < 5))
117+ (\_ -> Ref.modify (_ + 1) counterRef # liftEffect)
118+ # Om.runOm {} { exception: \_ -> pure 0 }
119+ # liftAff
120+ result `shouldEqual` 5
121+
122+ it "stops when condition returns false" do
123+ counterRef <- Ref.new 0 # liftEffect
124+ result <-
125+ repeating (constantDelay (Milliseconds 0.0) <> limitRetries 100)
126+ (\_ n -> pure (n < 3))
127+ (\_ -> Ref.modify (_ + 1) counterRef # liftEffect)
128+ # Om.runOm {} { exception: \_ -> pure 0 }
129+ # liftAff
130+ result `shouldEqual` 3
131+
132+ it "stops when policy exhausted" do
133+ counterRef <- Ref.new 0 # liftEffect
134+ result <-
135+ repeating (constantDelay (Milliseconds 0.0) <> limitRetries 3)
136+ (\_ _ -> pure true)
137+ (\_ -> Ref.modify (_ + 1) counterRef # liftEffect)
138+ # Om.runOm {} { exception: \_ -> pure 0 }
139+ # liftAff
140+ result `shouldEqual` 4
141+
142+ it "returns last result" do
143+ result <-
144+ repeating (constantDelay (Milliseconds 0.0) <> limitRetries 2)
145+ (\_ _ -> pure true)
146+ (\(RetryStatus s) -> pure ("attempt-" <> show s.iterNumber))
147+ # Om.runOm {} { exception: \_ -> pure "" }
148+ # liftAff
149+ result `shouldEqual` "attempt-2"