Skip to content

Commit b653f01

Browse files
authored
Add Operation Error for FakeClient and Property Based Tester (#10)
* Add Set and Delete Error * Add Operation Error Probability
1 parent 93fa539 commit b653f01

File tree

4 files changed

+201
-3
lines changed

4 files changed

+201
-3
lines changed

curator/fake_client.go

+30
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,15 @@ func (s *FakeZookeeper) GetApply(clientID FakeClientID) {
444444

445445
// SetApply ...
446446
func (s *FakeZookeeper) SetApply(clientID FakeClientID) {
447+
s.setApplyWithError(clientID, nil)
448+
}
449+
450+
// SetApplyError ...
451+
func (s *FakeZookeeper) SetApplyError(clientID FakeClientID) {
452+
s.setApplyWithError(clientID, zk.ErrConnectionClosed)
453+
}
454+
455+
func (s *FakeZookeeper) setApplyWithError(clientID FakeClientID, err error) {
447456
input := getActionWithType[SetInput](s, clientID, "Set")
448457

449458
node := s.findNode(input.Path)
@@ -465,6 +474,12 @@ func (s *FakeZookeeper) SetApply(clientID FakeClientID) {
465474

466475
s.notifyDataWatches(node, input.Path, zk.EventNodeDataChanged)
467476

477+
if err != nil {
478+
input.Callback(zk.SetResponse{}, err)
479+
s.ConnError(clientID)
480+
return
481+
}
482+
468483
input.Callback(zk.SetResponse{
469484
Zxid: s.Zxid,
470485
Stat: node.Stat,
@@ -473,6 +488,15 @@ func (s *FakeZookeeper) SetApply(clientID FakeClientID) {
473488

474489
// DeleteApply ...
475490
func (s *FakeZookeeper) DeleteApply(clientID FakeClientID) {
491+
s.deleteApplyWithError(clientID, nil)
492+
}
493+
494+
// DeleteApplyError ...
495+
func (s *FakeZookeeper) DeleteApplyError(clientID FakeClientID) {
496+
s.deleteApplyWithError(clientID, zk.ErrConnectionClosed)
497+
}
498+
499+
func (s *FakeZookeeper) deleteApplyWithError(clientID FakeClientID, err error) {
476500
input := getActionWithType[DeleteInput](s, clientID, "Delete")
477501
node := s.findNode(input.Path)
478502
if node == nil {
@@ -502,6 +526,12 @@ func (s *FakeZookeeper) DeleteApply(clientID FakeClientID) {
502526
s.notifyChildrenWatches(parent, stdpath.Dir(input.Path))
503527
s.notifyDataWatches(node, input.Path, zk.EventNodeDeleted)
504528

529+
if err != nil {
530+
input.Callback(zk.DeleteResponse{}, err)
531+
s.ConnError(clientID)
532+
return
533+
}
534+
505535
input.Callback(zk.DeleteResponse{
506536
Zxid: s.Zxid,
507537
}, nil)

curator/fake_client_test.go

+74
Original file line numberDiff line numberDiff line change
@@ -1488,3 +1488,77 @@ func TestFakeClient_Create_Child_of_Ephemeral_Error(t *testing.T) {
14881488
zk.ErrNoChildrenForEphemerals,
14891489
}, errors)
14901490
}
1491+
1492+
func TestFakeClient__Set_Error(t *testing.T) {
1493+
c := newFakeClientTest()
1494+
1495+
var errors []error
1496+
callback := func(client Client) {
1497+
client.Create("/worker", []byte("data01"), zk.FlagEphemeral, func(resp zk.CreateResponse, err error) {
1498+
c.addStep("create-resp")
1499+
errors = append(errors, err)
1500+
})
1501+
client.Set("/worker", []byte("data02"), 0, func(resp zk.SetResponse, err error) {
1502+
c.addStep("set-resp")
1503+
errors = append(errors, err)
1504+
})
1505+
}
1506+
1507+
c.startCuratorClient1(func(sess *Session) {
1508+
sess.Run(callback)
1509+
})
1510+
1511+
c.store.Begin(client1)
1512+
c.store.CreateApply(client1)
1513+
c.store.SetApplyError(client1)
1514+
1515+
c.store.Retry(client1)
1516+
1517+
assert.Equal(t, []error{
1518+
nil,
1519+
zk.ErrConnectionClosed,
1520+
}, errors)
1521+
assert.Equal(t, []string{
1522+
"create-resp",
1523+
"set-resp",
1524+
}, c.steps)
1525+
1526+
node := c.store.Root.Children[0]
1527+
assert.Equal(t, "worker", node.Name)
1528+
assert.Equal(t, "data02", string(node.Data))
1529+
}
1530+
1531+
func TestFakeClient__Delete_Error(t *testing.T) {
1532+
c := newFakeClientTest()
1533+
1534+
var errors []error
1535+
callback := func(client Client) {
1536+
client.Create("/worker", []byte("data01"), zk.FlagEphemeral, func(resp zk.CreateResponse, err error) {
1537+
c.addStep("create-resp")
1538+
errors = append(errors, err)
1539+
})
1540+
client.Delete("/worker", 0, func(resp zk.DeleteResponse, err error) {
1541+
c.addStep("delete-resp")
1542+
errors = append(errors, err)
1543+
})
1544+
}
1545+
1546+
c.startCuratorClient1(func(sess *Session) {
1547+
sess.Run(callback)
1548+
})
1549+
1550+
c.store.Begin(client1)
1551+
c.store.CreateApply(client1)
1552+
c.store.DeleteApplyError(client1)
1553+
1554+
assert.Equal(t, []error{
1555+
nil,
1556+
zk.ErrConnectionClosed,
1557+
}, errors)
1558+
assert.Equal(t, []string{
1559+
"create-resp",
1560+
"delete-resp",
1561+
}, c.steps)
1562+
1563+
assert.Equal(t, 0, len(c.store.Root.Children))
1564+
}

curator/fake_property.go

+56-3
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,53 @@ func (f *FakeZookeeperTester) doConnectionError(client FakeClientID) {
9090
}
9191
}
9292

93+
type runConfig struct {
94+
opsErrorPercent float64
95+
}
96+
97+
func (c runConfig) operationShouldError(randSource *rand.Rand) bool {
98+
if c.opsErrorPercent == 0 {
99+
return false
100+
}
101+
n := randSource.Intn(randMax)
102+
end := int(c.opsErrorPercent / 100.0 * randMax)
103+
if n < end {
104+
return true
105+
}
106+
return false
107+
}
108+
109+
func newRunConfig(options ...RunOption) runConfig {
110+
conf := runConfig{
111+
opsErrorPercent: 0.0,
112+
}
113+
for _, fn := range options {
114+
fn(&conf)
115+
}
116+
return conf
117+
}
118+
119+
// RunOption ...
120+
type RunOption func(conf *runConfig)
121+
122+
// WithRunOperationErrorPercentage ...
123+
func WithRunOperationErrorPercentage(percent float64) RunOption {
124+
return func(conf *runConfig) {
125+
conf.opsErrorPercent = percent
126+
}
127+
}
128+
93129
// RunSessionExpiredAndConnectionError ...
130+
//
131+
//revive:disable-next-line:cognitive-complexity,cyclomatic
94132
func (f *FakeZookeeperTester) RunSessionExpiredAndConnectionError(
95133
sessionExpiredPercentage float64,
96134
connectionErrorPercentage float64,
97135
numSteps int,
136+
options ...RunOption,
98137
) int {
138+
conf := newRunConfig(options...)
139+
99140
sessionExpiredEnd := int(sessionExpiredPercentage / 100.0 * randMax)
100141
connectionErrorEnd := int(connectionErrorPercentage / 100.0 * randMax)
101142

@@ -123,15 +164,27 @@ func (f *FakeZookeeperTester) RunSessionExpiredAndConnectionError(
123164
genericCmd := f.store.Pending[client][0]
124165
switch genericCmd.(type) {
125166
case CreateInput:
126-
f.store.CreateApply(client)
167+
if conf.operationShouldError(f.rand) {
168+
f.store.CreateApplyError(client)
169+
} else {
170+
f.store.CreateApply(client)
171+
}
127172
case GetInput:
128173
f.store.GetApply(client)
129174
case ChildrenInput:
130175
f.store.ChildrenApply(client)
131176
case DeleteInput:
132-
f.store.DeleteApply(client)
177+
if conf.operationShouldError(f.rand) {
178+
f.store.DeleteApplyError(client)
179+
} else {
180+
f.store.DeleteApply(client)
181+
}
133182
case SetInput:
134-
f.store.SetApply(client)
183+
if conf.operationShouldError(f.rand) {
184+
f.store.SetApplyError(client)
185+
} else {
186+
f.store.SetApply(client)
187+
}
135188
case RetryInput:
136189
f.store.Retry(client)
137190
default:

curator/fake_property_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package curator
33
import (
44
"errors"
55
"fmt"
6+
"math/rand"
67
"strconv"
78
"testing"
89
"time"
@@ -188,3 +189,43 @@ func TestFakeZookeeperTester_Master_Lock__Multi_Times(t *testing.T) {
188189
)
189190
}
190191
}
192+
193+
func TestFakeZookeeperTester_Master_Lock__Multi_Times__With_Ops_Error(t *testing.T) {
194+
for i := 0; i < 1000; i++ {
195+
seed := time.Now().UnixNano()
196+
fmt.Println("SEED:", seed)
197+
198+
store := NewFakeZookeeper()
199+
tester := NewFakeZookeeperTester(store, []FakeClientID{
200+
client1,
201+
client2,
202+
client3,
203+
}, seed)
204+
205+
newSimpleLock(store, client1)
206+
newSimpleLock(store, client2)
207+
newSimpleLock(store, client3)
208+
209+
tester.Begin()
210+
211+
tester.RunSessionExpiredAndConnectionError(
212+
10,
213+
10,
214+
1000,
215+
WithRunOperationErrorPercentage(15),
216+
)
217+
}
218+
}
219+
220+
func TestRunConfig(t *testing.T) {
221+
c := newRunConfig(WithRunOperationErrorPercentage(20))
222+
223+
source := rand.New(rand.NewSource(1234))
224+
trueCount := 0
225+
for i := 0; i < 1000; i++ {
226+
if c.operationShouldError(source) {
227+
trueCount++
228+
}
229+
}
230+
assert.Equal(t, 214, trueCount)
231+
}

0 commit comments

Comments
 (0)