@@ -4,9 +4,128 @@ defmodule Lightning.RetryTest do
4
4
5
5
alias Lightning.Retry
6
6
7
+ @ moduletag capture_log: true
8
+
7
9
setup :set_mox_from_context
8
10
setup :verify_on_exit!
9
11
12
+ describe "retriable_error?/1" do
13
+ test "returns true for {:error, %DBConnection.ConnectionError{}}" do
14
+ assert Retry . retriable_error? ( { :error , % DBConnection.ConnectionError { } } )
15
+ end
16
+
17
+ test "returns false for non-DB errors" do
18
+ refute Retry . retriable_error? ( { :error , :nope } )
19
+ refute Retry . retriable_error? ( :anything )
20
+ end
21
+ end
22
+
23
+ describe "with_retry/2 timeout branch" do
24
+ test "hits :timeout when timeout_ms is 0" do
25
+ attempts = :counters . new ( 1 , [ ] )
26
+
27
+ result =
28
+ Retry . with_retry (
29
+ fn ->
30
+ :counters . add ( attempts , 1 , 1 )
31
+ { :error , % DBConnection.ConnectionError { message: "slow" } }
32
+ end ,
33
+ timeout_ms: 0 ,
34
+ max_attempts: 5 ,
35
+ initial_delay_ms: 0 ,
36
+ jitter: false
37
+ )
38
+
39
+ assert { :error , % DBConnection.ConnectionError { } } = result
40
+ assert :counters . get ( attempts , 1 ) == 1
41
+ end
42
+ end
43
+
44
+ describe "with_retry/2 rescue path" do
45
+ test "wraps raised DBConnection.ConnectionError into {:error, e}" do
46
+ attempts = :counters . new ( 1 , [ ] )
47
+
48
+ result =
49
+ Retry . with_retry (
50
+ fn ->
51
+ :counters . add ( attempts , 1 , 1 )
52
+ raise DBConnection.ConnectionError , message: "boom"
53
+ end ,
54
+ max_attempts: 1 ,
55
+ initial_delay_ms: 0 ,
56
+ jitter: false
57
+ )
58
+
59
+ assert { :error , % DBConnection.ConnectionError { } } = result
60
+ assert :counters . get ( attempts , 1 ) == 1
61
+ end
62
+ end
63
+
64
+ describe "with_retry/2 jitter path" do
65
+ test "executes jittered delay when jitter: true and base_delay > 0" do
66
+ attempts = :counters . new ( 1 , [ ] )
67
+
68
+ result =
69
+ Retry . with_retry (
70
+ fn ->
71
+ :counters . add ( attempts , 1 , 1 )
72
+ { :error , % DBConnection.ConnectionError { message: "flaky" } }
73
+ end ,
74
+ max_attempts: 2 ,
75
+ initial_delay_ms: 8 ,
76
+ jitter: true ,
77
+ timeout_ms: 1_000
78
+ )
79
+
80
+ assert { :error , % DBConnection.ConnectionError { } } = result
81
+ assert :counters . get ( attempts , 1 ) == 2
82
+ end
83
+ end
84
+
85
+ describe "with_retry/2 option coercions and clamps" do
86
+ test "string options are coerced (to_int/to_float) and respected" do
87
+ attempts = :counters . new ( 1 , [ ] )
88
+
89
+ result =
90
+ Retry . with_retry (
91
+ fn ->
92
+ :counters . add ( attempts , 1 , 1 )
93
+ { :error , % DBConnection.ConnectionError { message: "nope" } }
94
+ end ,
95
+ max_attempts: "2" ,
96
+ initial_delay_ms: "0" ,
97
+ max_delay_ms: "0" ,
98
+ backoff_factor: "1.5" ,
99
+ timeout_ms: "50" ,
100
+ jitter: false
101
+ )
102
+
103
+ assert { :error , % DBConnection.ConnectionError { } } = result
104
+ assert :counters . get ( attempts , 1 ) == 2
105
+ end
106
+
107
+ test "invalid numeric options are clamped to safe mins" do
108
+ attempts = :counters . new ( 1 , [ ] )
109
+
110
+ result =
111
+ Retry . with_retry (
112
+ fn ->
113
+ :counters . add ( attempts , 1 , 1 )
114
+ { :error , % DBConnection.ConnectionError { message: "still no" } }
115
+ end ,
116
+ max_attempts: 0 ,
117
+ initial_delay_ms: - 10 ,
118
+ max_delay_ms: - 5 ,
119
+ backoff_factor: 0.0 ,
120
+ timeout_ms: - 1 ,
121
+ jitter: false
122
+ )
123
+
124
+ assert { :error , % DBConnection.ConnectionError { } } = result
125
+ assert :counters . get ( attempts , 1 ) == 1
126
+ end
127
+ end
128
+
10
129
describe "with_retry/2" do
11
130
test "returns success immediately when function succeeds" do
12
131
assert { :ok , "success" } = Retry . with_retry ( fn -> { :ok , "success" } end )
0 commit comments