Skip to content

Commit

Permalink
tests/robustness: Move key into dedicated options to allow to impleme…
Browse files Browse the repository at this point in the history
…nt proper ranges

Signed-off-by: Marek Siarkowicz <[email protected]>
  • Loading branch information
serathius committed Jun 22, 2023
1 parent 3f09a51 commit f7831e2
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 68 deletions.
26 changes: 13 additions & 13 deletions tests/robustness/model/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func describeEtcdResponse(request EtcdRequest, response MaybeEtcdResponse) strin
func describeEtcdRequest(request EtcdRequest) string {
switch request.Type {
case Range:
return describeRangeRequest(request.Range.Key, request.Range.Revision, request.Range.RangeOptions)
return describeRangeRequest(request.Range.RangeOptions, request.Range.Revision)
case Txn:
onSuccess := describeEtcdOperations(request.Txn.OperationsOnSuccess)
if len(request.Txn.Conditions) != 0 {
Expand Down Expand Up @@ -105,20 +105,20 @@ func describeTxnResponse(request *TxnRequest, response *TxnResponse) string {
func describeEtcdOperation(op EtcdOperation) string {
switch op.Type {
case RangeOperation:
return describeRangeRequest(op.Key, 0, op.RangeOptions)
return describeRangeRequest(op.Range, 0)
case PutOperation:
if op.LeaseID != 0 {
return fmt.Sprintf("put(%q, %s, %d)", op.Key, describeValueOrHash(op.Value), op.LeaseID)
if op.Put.LeaseID != 0 {
return fmt.Sprintf("put(%q, %s, %d)", op.Put.Key, describeValueOrHash(op.Put.Value), op.Put.LeaseID)
}
return fmt.Sprintf("put(%q, %s)", op.Key, describeValueOrHash(op.Value))
return fmt.Sprintf("put(%q, %s)", op.Put.Key, describeValueOrHash(op.Put.Value))
case DeleteOperation:
return fmt.Sprintf("delete(%q)", op.Key)
return fmt.Sprintf("delete(%q)", op.Delete.Key)
default:
return fmt.Sprintf("<! unknown op: %q !>", op.Type)
}
}

func describeRangeRequest(key string, revision int64, opts RangeOptions) string {
func describeRangeRequest(opts RangeOptions, revision int64) string {
kwargs := []string{}
if revision != 0 {
kwargs = append(kwargs, fmt.Sprintf("rev=%d", revision))
Expand All @@ -131,21 +131,21 @@ func describeRangeRequest(key string, revision int64, opts RangeOptions) string
command = "range"
}
if len(kwargs) == 0 {
return fmt.Sprintf("%s(%q)", command, key)
return fmt.Sprintf("%s(%q)", command, opts.Key)
}
return fmt.Sprintf("%s(%q, %s)", command, key, strings.Join(kwargs, ", "))
return fmt.Sprintf("%s(%q, %s)", command, opts.Key, strings.Join(kwargs, ", "))
}

func describeEtcdOperationResponse(req EtcdOperation, resp EtcdOperationResult) string {
switch req.Type {
func describeEtcdOperationResponse(op EtcdOperation, resp EtcdOperationResult) string {
switch op.Type {
case RangeOperation:
return describeRangeResponse(req.RangeOptions, resp.RangeResponse)
return describeRangeResponse(op.Range, resp.RangeResponse)
case PutOperation:
return fmt.Sprintf("ok")
case DeleteOperation:
return fmt.Sprintf("deleted: %d", resp.Deleted)
default:
return fmt.Sprintf("<! unknown op: %q !>", req.Type)
return fmt.Sprintf("<! unknown op: %q !>", op.Type)
}
}

Expand Down
10 changes: 5 additions & 5 deletions tests/robustness/model/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
"errors"
"testing"

"github.com/stretchr/testify/assert"

"go.etcd.io/etcd/api/v3/mvccpb"

"github.com/stretchr/testify/assert"
)

func TestModelDescribe(t *testing.T) {
Expand Down Expand Up @@ -95,17 +95,17 @@ func TestModelDescribe(t *testing.T) {
expectDescribe: `if(mod_rev(key9)==9).then(put("key9", "99")) -> err: "failed"`,
},
{
req: txnRequest([]EtcdCondition{{Key: "key9b", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Key: "key9b", Value: ValueOrHash{Value: "991"}}}, []EtcdOperation{{Type: RangeOperation, Key: "key9b"}}),
req: txnRequest([]EtcdCondition{{Key: "key9b", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9b", Value: ValueOrHash{Value: "991"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "key9b"}}}),
resp: txnResponse([]EtcdOperationResult{{}}, true, 10),
expectDescribe: `if(mod_rev(key9b)==9).then(put("key9b", "991")).else(get("key9b")) -> success(ok), rev: 10`,
},
{
req: txnRequest([]EtcdCondition{{Key: "key9c", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Key: "key9c", Value: ValueOrHash{Value: "992"}}}, []EtcdOperation{{Type: RangeOperation, Key: "key9c"}}),
req: txnRequest([]EtcdCondition{{Key: "key9c", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9c", Value: ValueOrHash{Value: "992"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "key9c"}}}),
resp: txnResponse([]EtcdOperationResult{{RangeResponse: RangeResponse{KVs: []KeyValue{{Key: "key9c", ValueRevision: ValueRevision{Value: ValueOrHash{Value: "993"}, ModRevision: 10}}}}}}, false, 10),
expectDescribe: `if(mod_rev(key9c)==9).then(put("key9c", "992")).else(get("key9c")) -> failure("993"), rev: 10`,
},
{
req: txnRequest(nil, []EtcdOperation{{Type: RangeOperation, Key: "10"}, {Type: PutOperation, Key: "11", Value: ValueOrHash{Value: "111"}}, {Type: DeleteOperation, Key: "12"}}, nil),
req: txnRequest(nil, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "10"}}, {Type: PutOperation, Put: PutOptions{Key: "11", Value: ValueOrHash{Value: "111"}}}, {Type: DeleteOperation, Delete: DeleteOptions{Key: "12"}}}, nil),
resp: txnResponse([]EtcdOperationResult{{RangeResponse: RangeResponse{KVs: []KeyValue{{ValueRevision: ValueRevision{Value: ValueOrHash{Value: "110"}}}}}}, {}, {Deleted: 1}}, true, 10),
expectDescribe: `get("10"), put("11", "111"), delete("12") -> "110", ok, deleted: 1, rev: 10`,
},
Expand Down
51 changes: 30 additions & 21 deletions tests/robustness/model/deterministic.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
switch request.Type {
case Range:
if request.Range.Revision == 0 || request.Range.Revision == s.Revision {
resp := s.getRange(request.Range.Key, request.Range.RangeOptions)
resp := s.getRange(request.Range.RangeOptions)
return s, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Range: &resp, Revision: s.Revision}}
} else {
if request.Range.Revision > s.Revision {
Expand All @@ -121,27 +121,27 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
switch op.Type {
case RangeOperation:
opResp[i] = EtcdOperationResult{
RangeResponse: s.getRange(op.Key, op.RangeOptions),
RangeResponse: s.getRange(op.Range),
}
case PutOperation:
_, leaseExists := s.Leases[op.LeaseID]
if op.LeaseID != 0 && !leaseExists {
_, leaseExists := s.Leases[op.Put.LeaseID]
if op.Put.LeaseID != 0 && !leaseExists {
break
}
s.KeyValues[op.Key] = ValueRevision{
Value: op.Value,
s.KeyValues[op.Put.Key] = ValueRevision{
Value: op.Put.Value,
ModRevision: s.Revision + 1,
}
increaseRevision = true
s = detachFromOldLease(s, op.Key)
s = detachFromOldLease(s, op.Put.Key)
if leaseExists {
s = attachToNewLease(s, op.LeaseID, op.Key)
s = attachToNewLease(s, op.Put.LeaseID, op.Put.Key)
}
case DeleteOperation:
if _, ok := s.KeyValues[op.Key]; ok {
delete(s.KeyValues, op.Key)
if _, ok := s.KeyValues[op.Delete.Key]; ok {
delete(s.KeyValues, op.Delete.Key)
increaseRevision = true
s = detachFromOldLease(s, op.Key)
s = detachFromOldLease(s, op.Delete.Key)
opResp[i].Deleted = 1
}
default:
Expand Down Expand Up @@ -185,14 +185,14 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
}
}

func (s EtcdState) getRange(key string, options RangeOptions) RangeResponse {
func (s EtcdState) getRange(options RangeOptions) RangeResponse {
response := RangeResponse{
KVs: []KeyValue{},
}
if options.WithPrefix {
var count int64
for k, v := range s.KeyValues {
if strings.HasPrefix(k, key) {
if strings.HasPrefix(k, options.Key) {
response.KVs = append(response.KVs, KeyValue{Key: k, ValueRevision: v})
count += 1
}
Expand All @@ -205,10 +205,10 @@ func (s EtcdState) getRange(key string, options RangeOptions) RangeResponse {
}
response.Count = count
} else {
value, ok := s.KeyValues[key]
value, ok := s.KeyValues[options.Key]
if ok {
response.KVs = append(response.KVs, KeyValue{
Key: key,
Key: options.Key,
ValueRevision: value,
})
response.Count = 1
Expand Down Expand Up @@ -251,16 +251,26 @@ type EtcdRequest struct {
}

type RangeRequest struct {
Key string
RangeOptions
Revision int64
}

type RangeOptions struct {
Key string
WithPrefix bool
Limit int64
}

type PutOptions struct {
Key string
Value ValueOrHash
LeaseID int64
}

type DeleteOptions struct {
Key string
}

type TxnRequest struct {
Conditions []EtcdCondition
OperationsOnSuccess []EtcdOperation
Expand All @@ -273,11 +283,10 @@ type EtcdCondition struct {
}

type EtcdOperation struct {
Type OperationType
Key string
RangeOptions
Value ValueOrHash
LeaseID int64
Type OperationType
Range RangeOptions
Put PutOptions
Delete DeleteOptions
}

type OperationType string
Expand Down
22 changes: 15 additions & 7 deletions tests/robustness/model/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,23 @@ func toEtcdCondition(cmp clientv3.Cmp) (cond EtcdCondition) {
}

func toEtcdOperation(option clientv3.Op) (op EtcdOperation) {
op.Key = string(option.KeyBytes())
switch {
case option.IsGet():
op.Type = RangeOperation
op.Range = RangeOptions{
Key: string(option.KeyBytes()),
}
case option.IsPut():
op.Type = PutOperation
op.Value = ValueOrHash{Value: string(option.ValueBytes())}
op.Put = PutOptions{
Key: string(option.KeyBytes()),
Value: ValueOrHash{Value: string(option.ValueBytes())},
}
case option.IsDelete():
op.Type = DeleteOperation
op.Delete = DeleteOptions{
Key: string(option.KeyBytes()),
}
default:
panic("Unsupported operation")
}
Expand Down Expand Up @@ -346,7 +354,7 @@ func rangeRequest(key string, withPrefix bool, limit int64) EtcdRequest {
}

func staleRangeRequest(key string, withPrefix bool, limit, revision int64) EtcdRequest {
return EtcdRequest{Type: Range, Range: &RangeRequest{Key: key, RangeOptions: RangeOptions{WithPrefix: withPrefix, Limit: limit}, Revision: revision}}
return EtcdRequest{Type: Range, Range: &RangeRequest{RangeOptions: RangeOptions{Key: key, WithPrefix: withPrefix, Limit: limit}, Revision: revision}}
}

func emptyGetResponse(revision int64) MaybeEtcdResponse {
Expand Down Expand Up @@ -381,15 +389,15 @@ func partialResponse(revision int64) MaybeEtcdResponse {
}

func putRequest(key, value string) EtcdRequest {
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: PutOperation, Key: key, Value: ToValueOrHash(value)}}}}
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: key, Value: ToValueOrHash(value)}}}}}
}

func putResponse(revision int64) MaybeEtcdResponse {
return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: []EtcdOperationResult{{}}}, Revision: revision}}
}

func deleteRequest(key string) EtcdRequest {
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: DeleteOperation, Key: key}}}}
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: DeleteOperation, Delete: DeleteOptions{Key: key}}}}}
}

func deleteResponse(deleted int64, revision int64) MaybeEtcdResponse {
Expand All @@ -412,7 +420,7 @@ func compareRevision(key string, expectedRevision int64) *EtcdCondition {
}

func putOperation(key, value string) *EtcdOperation {
return &EtcdOperation{Type: PutOperation, Key: key, Value: ToValueOrHash(value)}
return &EtcdOperation{Type: PutOperation, Put: PutOptions{Key: key, Value: ToValueOrHash(value)}}
}

func txnRequestSingleOperation(cond *EtcdCondition, onSuccess, onFailure *EtcdOperation) EtcdRequest {
Expand Down Expand Up @@ -448,7 +456,7 @@ func txnResponse(result []EtcdOperationResult, succeeded bool, revision int64) M
}

func putWithLeaseRequest(key, value string, leaseID int64) EtcdRequest {
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: PutOperation, Key: key, Value: ToValueOrHash(value), LeaseID: leaseID}}}}
return EtcdRequest{Type: Txn, Txn: &TxnRequest{OperationsOnSuccess: []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: key, Value: ToValueOrHash(value), LeaseID: leaseID}}}}}
}

func leaseGrantRequest(leaseID int64) EtcdRequest {
Expand Down
17 changes: 12 additions & 5 deletions tests/robustness/model/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,18 @@ func (r *EtcdReplay) next() (request EtcdRequest, revision int64, index int) {
operations := []EtcdOperation{}
for r.eventHistory[index].Revision == revision {
event := r.eventHistory[index]
operations = append(operations, EtcdOperation{
Type: event.Type,
Key: event.Key,
Value: event.Value,
})
switch event.Type {
case PutOperation:
operations = append(operations, EtcdOperation{
Type: event.Type,
Put: PutOptions{Key: event.Key, Value: event.Value},
})
case DeleteOperation:
operations = append(operations, EtcdOperation{
Type: event.Type,
Delete: DeleteOptions{Key: event.Key},
})
}
index++
}
return EtcdRequest{
Expand Down
20 changes: 8 additions & 12 deletions tests/robustness/traffic/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,22 +254,18 @@ func ToWatchResponse(r clientv3.WatchResponse, baseTime time.Time) WatchResponse
return resp
}

func toWatchEvent(event clientv3.Event) model.WatchEvent {
var op model.OperationType
func toWatchEvent(event clientv3.Event) (watch model.WatchEvent) {
watch.Revision = event.Kv.ModRevision
watch.Key = string(event.Kv.Key)
watch.Value = model.ToValueOrHash(string(event.Kv.Value))

switch event.Type {
case mvccpb.PUT:
op = model.PutOperation
watch.Type = model.PutOperation
case mvccpb.DELETE:
op = model.DeleteOperation
watch.Type = model.DeleteOperation
default:
panic(fmt.Sprintf("Unexpected event type: %s", event.Type))
}
return model.WatchEvent{
Revision: event.Kv.ModRevision,
Event: model.Event{
Type: op,
Key: string(event.Kv.Key),
Value: model.ToValueOrHash(string(event.Kv.Value)),
},
}
return watch
}
4 changes: 2 additions & 2 deletions tests/robustness/validate/patch_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ func matchWatchEvent(request *model.TxnRequest, watchEvents map[model.Event]traf
if etcdOp.Type == model.PutOperation {
event, ok := watchEvents[model.Event{
Type: etcdOp.Type,
Key: etcdOp.Key,
Value: etcdOp.Value,
Key: etcdOp.Put.Key,
Value: etcdOp.Put.Value,
}]
if ok {
return &event
Expand Down
3 changes: 0 additions & 3 deletions tests/robustness/validate/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ func validateUnique(t *testing.T, expectUniqueRevision bool, report traffic.Clie
key string
}{event.Revision, event.Key}
}

if _, found := uniqueOperations[key]; found {

t.Errorf("Broke watch guarantee: Unique - an event will never appear on a watch twice, key: %q, revision: %d, client: %d", event.Key, event.Revision, report.ClientId)
}
uniqueOperations[key] = struct{}{}
Expand Down Expand Up @@ -138,7 +136,6 @@ func validateEventsMatch(t *testing.T, reports []traffic.ClientReport) {
for _, r := range reports {
for _, resp := range r.Watch {
for _, event := range resp.Events {

rk := revisionKey{key: event.Key, revision: event.Revision}
if prev, found := revisionKeyToEvent[rk]; found {
if prev.WatchEvent != event {
Expand Down

0 comments on commit f7831e2

Please sign in to comment.