etcd/tests/robustness/validate/operations_test.go
Akshay Nanavare 8eb91d0e15 add error constants in validate pkg
Signed-off-by: Akshay Nanavare <nakshay303@gmail.com>
2024-05-14 20:19:08 +05:30

296 lines
6.6 KiB
Go

// Copyright 2024 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//nolint:unparam
package validate
import (
"fmt"
"testing"
"github.com/anishathalye/porcupine"
"go.uber.org/zap/zaptest"
"go.etcd.io/etcd/tests/v3/robustness/model"
)
func TestValidateSerializableOperations(t *testing.T) {
tcs := []struct {
name string
persistedRequests []model.EtcdRequest
operations []porcupine.Operation
expectError string
}{
{
name: "Success",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 1, 0),
Output: rangeResponse(0),
},
{
Input: rangeRequest("a", "z", 2, 0),
Output: rangeResponse(1, keyValue("a", "1", 2)),
},
{
Input: rangeRequest("a", "z", 3, 0),
Output: rangeResponse(2,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
),
},
{
Input: rangeRequest("a", "z", 4, 0),
Output: rangeResponse(3,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
keyValue("c", "3", 4),
),
},
{
Input: rangeRequest("a", "z", 4, 3),
Output: rangeResponse(3,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
keyValue("c", "3", 4),
),
},
{
Input: rangeRequest("a", "z", 4, 4),
Output: rangeResponse(3,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
keyValue("c", "3", 4),
),
},
{
Input: rangeRequest("a", "z", 4, 2),
Output: rangeResponse(3,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
),
},
{
Input: rangeRequest("b\x00", "z", 4, 2),
Output: rangeResponse(1,
keyValue("c", "3", 4),
),
},
{
Input: rangeRequest("b", "", 4, 0),
Output: rangeResponse(1,
keyValue("b", "2", 3),
),
},
{
Input: rangeRequest("b", "", 2, 0),
Output: rangeResponse(0),
},
},
},
{
name: "Invalid order",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 4, 0),
Output: rangeResponse(3,
keyValue("c", "3", 4),
keyValue("b", "2", 3),
keyValue("a", "1", 2),
),
},
},
expectError: errRespNotMatched.Error(),
},
{
name: "Invalid count",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 1, 0),
Output: rangeResponse(1),
},
},
expectError: errRespNotMatched.Error(),
},
{
name: "Invalid keys",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 2, 0),
Output: rangeResponse(3,
keyValue("b", "2", 3),
),
},
},
expectError: errRespNotMatched.Error(),
},
{
name: "Invalid revision",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 2, 0),
Output: rangeResponse(3,
keyValue("a", "1", 2),
keyValue("b", "2", 3),
),
},
},
expectError: errRespNotMatched.Error(),
},
{
name: "Error",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 2, 0),
Output: errorResponse(model.ErrEtcdFutureRev),
},
{
Input: rangeRequest("a", "z", 2, 0),
Output: errorResponse(fmt.Errorf("timeout")),
},
},
},
{
name: "Future rev returned",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 6, 0),
Output: errorResponse(model.ErrEtcdFutureRev),
},
},
},
{
name: "Future rev success",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 6, 0),
Output: rangeResponse(0),
},
},
expectError: errFutureRevRespRequested.Error(),
},
{
name: "Future rev failure",
persistedRequests: []model.EtcdRequest{
putRequest("a", "1"),
putRequest("b", "2"),
putRequest("c", "3"),
},
operations: []porcupine.Operation{
{
Input: rangeRequest("a", "z", 6, 0),
Output: errorResponse(fmt.Errorf("timeout")),
},
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
replay := model.NewReplay(tc.persistedRequests)
err := validateSerializableOperations(zaptest.NewLogger(t), tc.operations, replay)
var errStr string
if err != nil {
errStr = err.Error()
}
if errStr != tc.expectError {
t.Errorf("validateSerializableOperations(...), got: %q, want: %q", err, tc.expectError)
}
})
}
}
func rangeRequest(start, end string, rev, limit int64) model.EtcdRequest {
return model.EtcdRequest{
Type: model.Range,
Range: &model.RangeRequest{
RangeOptions: model.RangeOptions{
Start: start,
End: end,
Limit: limit,
},
Revision: rev,
},
}
}
func rangeResponse(count int64, kvs ...model.KeyValue) model.MaybeEtcdResponse {
if kvs == nil {
kvs = []model.KeyValue{}
}
return model.MaybeEtcdResponse{
EtcdResponse: model.EtcdResponse{
Range: &model.RangeResponse{
KVs: kvs,
Count: count,
},
},
}
}
func errorResponse(err error) model.MaybeEtcdResponse {
return model.MaybeEtcdResponse{
Error: err.Error(),
}
}
func keyValue(key, value string, rev int64) model.KeyValue {
return model.KeyValue{
Key: key,
ValueRevision: model.ValueRevision{
Value: model.ToValueOrHash(value),
ModRevision: rev,
},
}
}