1defmodule DBConnection.BackoffTest do
2  use ExUnit.Case, async: true
3
4  alias DBConnection.Backoff
5
6  @moduletag backoff_min: 1_000
7  @moduletag backoff_max: 30_000
8
9  @tag backoff_type: :exp
10  test "exponential backoffs aways in [min, max]", context do
11    backoff = new(context)
12    {delays, _} = backoff(backoff, 20)
13    assert Enum.all?(delays, fn(delay) ->
14      delay >= context[:backoff_min] and delay <= context[:backoff_max]
15    end)
16  end
17
18  @tag backoff_type: :exp
19  test "exponential backoffs double until max", context do
20    backoff = new(context)
21    {delays, _} = backoff(backoff, 20)
22    Enum.reduce(delays, fn(next, prev) ->
23      assert div(next, 2) == prev or next == context[:backoff_max]
24      next
25    end)
26  end
27
28  @tag backoff_type: :exp
29  test "exponential backoffs reset to min", context do
30    backoff = new(context)
31    {[delay | _], backoff} = backoff(backoff, 20)
32    assert delay == context[:backoff_min]
33
34    backoff = Backoff.reset(backoff)
35    {[delay], _} = backoff(backoff, 1)
36    assert delay == context[:backoff_min]
37  end
38
39  @tag backoff_type: :rand
40  test "random backoffs aways in [min, max]", context do
41    backoff = new(context)
42    {delays, _} = backoff(backoff, 20)
43    assert Enum.all?(delays, fn(delay) ->
44      delay >= context[:backoff_min] and delay <= context[:backoff_max]
45    end)
46  end
47
48  @tag backoff_type: :rand
49  test "random backoffs are not all the same value", context do
50    backoff = new(context)
51    {delays, _} = backoff(backoff, 20)
52    ## If the stars align this test could fail ;)
53    refute Enum.all?(delays, &(hd(delays) == &1))
54  end
55
56  @tag backoff_type: :rand
57  test "random backoffs repeat", context do
58    backoff = new(context)
59    assert backoff(backoff, 20) == backoff(backoff, 20)
60  end
61
62  @tag backoff_type: :rand_exp
63  test "random exponential backoffs aways in [min, max]", context do
64    backoff = new(context)
65    {delays, _} = backoff(backoff, 20)
66    assert Enum.all?(delays, fn(delay) ->
67      delay >= context[:backoff_min] and delay <= context[:backoff_max]
68    end)
69  end
70
71  @tag backoff_type: :rand_exp
72  test "random exponential backoffs increase until a third of max", context do
73    backoff = new(context)
74    {delays, _} = backoff(backoff, 20)
75    Enum.reduce(delays, fn(next, prev) ->
76      assert next >= prev or (next >= div(context[:backoff_max], 3))
77      next
78    end)
79  end
80
81  @tag backoff_type: :rand_exp
82  test "random exponential backoffs repeat", context do
83    backoff = new(context)
84    assert backoff(backoff, 20) == backoff(backoff, 20)
85  end
86
87  @tag backoff_type: :rand_exp
88  test "random exponential backoffs reset in [min, min * 3]", context do
89    backoff = new(context)
90    {[delay | _], backoff} = backoff(backoff, 20)
91    assert delay in context[:backoff_min]..(context[:backoff_min]*3)
92
93    backoff = Backoff.reset(backoff)
94    {[delay], _} = backoff(backoff, 1)
95    assert delay in context[:backoff_min]..(context[:backoff_min]*3)
96  end
97
98  ## Helpers
99
100  def new(context) do
101    Backoff.new(Enum.into(context, []))
102  end
103
104  defp backoff(backoff, n) do
105    Enum.map_reduce(1..n, backoff, fn(_, acc) -> Backoff.backoff(acc) end)
106  end
107end
108