-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmoonbreaker_spec.lua
125 lines (107 loc) · 4.02 KB
/
moonbreaker_spec.lua
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
local moonbreaker = require "moonbreaker"
local mocks = require "spec.mocks"
describe("moonbreaker", function()
local limit, timeout = 5, 30
local clock, breaker
before_each(function()
clock = mocks.Clock()
breaker = moonbreaker {
limit = limit,
timeout = timeout,
now = clock,
}
end)
local function failure()
return nil, "error"
end
local function exceptional()
error("omg")
end
describe("state()", function()
it("should be closed initially", function()
assert.are.same(moonbreaker.states.closed, breaker:state())
end)
it("should remain closed after just a couple of failures", function()
local fun = breaker(failure)
for _ = 1, limit - 1 do fun() end
assert.are.same(moonbreaker.states.closed, breaker:state())
end)
it("should open after too many failures", function()
local fun = breaker(failure)
for _ = 1, limit do fun() end
assert.are.same(moonbreaker.states.open, breaker:state())
end)
it("should be open before timeout", function()
local fun = breaker(failure)
for _ = 1, limit do fun() end
clock:advance(timeout - 1)
assert.are.same(moonbreaker.states.open, breaker:state())
end)
it("should be half-open after timeout", function()
local fun = breaker(failure)
for _ = 1, limit do fun() end
clock:advance(timeout)
assert.are.same(moonbreaker.states.half_open, breaker:state())
end)
it("should be open after failed test", function()
local fun = breaker(failure)
for _ = 1, limit do fun() end
clock:advance(timeout)
fun()
assert.are.same(moonbreaker.states.open, breaker:state())
end)
it("should close after recovery", function()
local service = mocks.Service()
local fun = breaker(service)
service:crush()
for _ = 1, limit do fun() end
clock:advance(timeout)
service:repair()
for _ = 1, limit do fun() end
assert.are.same(moonbreaker.states.closed, breaker:state())
end)
end)
context("proxying", function()
it("should call target function with the same arguments", function()
local target = spy.new()
local fun = breaker(target)
fun(1, nil, 3)
assert.spy(target).was.called_with(1, nil, 3)
end)
it("should call target function and return the same values", function()
local fun = breaker(function() return 1, nil, 3 end)
assert.are.same({1, nil, 3}, {fun()})
end)
it("should return original error after just a couple of failures",
function()
local fun = breaker(failure)
for _ = 1, limit - 1 do
assert.are.same({nil, "error"}, {fun()})
end
end)
it("should call function to try to recover after timeout", function()
local target = spy.new(failure)
local fun = breaker(target)
for _ = 1, limit do fun() end
clock:advance(timeout)
fun("secret")
assert.spy(target).was.called_with("secret")
end)
it("should rethrow exception", function()
local fun = breaker(exceptional)
assert.has_error(fun)
end)
end)
context("errors", function()
it("should open after too many failures", function()
local fun = breaker(failure)
for _ = 1, limit do fun() end
assert.are.same({nil, moonbreaker.errors.open}, {fun()})
end)
it("should open after too many exceptions", function()
local fun = breaker(exceptional)
for _ = 1, limit do pcall(fun) end
assert.are.same({nil, moonbreaker.errors.open}, {fun()})
end)
end)
end)