mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #15059 from serathius/linearizability-operations
tests: Rewrite etcd requests to use operations
This commit is contained in:
commit
a992fb5e92
@ -46,17 +46,21 @@ func (h *appendableHistory) AppendGet(key string, start, end time.Time, resp *cl
|
|||||||
if len(resp.Kvs) == 1 {
|
if len(resp.Kvs) == 1 {
|
||||||
readData = string(resp.Kvs[0].Value)
|
readData = string(resp.Kvs[0].Value)
|
||||||
}
|
}
|
||||||
|
var revision int64
|
||||||
|
if resp != nil && resp.Header != nil {
|
||||||
|
revision = resp.Header.Revision
|
||||||
|
}
|
||||||
h.successful = append(h.successful, porcupine.Operation{
|
h.successful = append(h.successful, porcupine.Operation{
|
||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: EtcdRequest{Op: Get, Key: key},
|
Input: getRequest(key),
|
||||||
Call: start.UnixNano(),
|
Call: start.UnixNano(),
|
||||||
Output: EtcdResponse{GetData: readData, Revision: resp.Header.Revision},
|
Output: getResponse(readData, revision),
|
||||||
Return: end.UnixNano(),
|
Return: end.UnixNano(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *appendableHistory) AppendPut(key, value string, start, end time.Time, resp *clientv3.PutResponse, err error) {
|
func (h *appendableHistory) AppendPut(key, value string, start, end time.Time, resp *clientv3.PutResponse, err error) {
|
||||||
request := EtcdRequest{Op: Put, Key: key, PutData: value}
|
request := putRequest(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.appendFailed(request, start, err)
|
h.appendFailed(request, start, err)
|
||||||
return
|
return
|
||||||
@ -67,15 +71,15 @@ func (h *appendableHistory) AppendPut(key, value string, start, end time.Time, r
|
|||||||
}
|
}
|
||||||
h.successful = append(h.successful, porcupine.Operation{
|
h.successful = append(h.successful, porcupine.Operation{
|
||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: EtcdRequest{Op: Put, Key: key, PutData: value},
|
Input: request,
|
||||||
Call: start.UnixNano(),
|
Call: start.UnixNano(),
|
||||||
Output: EtcdResponse{Err: err, Revision: revision},
|
Output: putResponse(revision),
|
||||||
Return: end.UnixNano(),
|
Return: end.UnixNano(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *appendableHistory) AppendDelete(key string, start, end time.Time, resp *clientv3.DeleteResponse, err error) {
|
func (h *appendableHistory) AppendDelete(key string, start, end time.Time, resp *clientv3.DeleteResponse, err error) {
|
||||||
request := EtcdRequest{Op: Delete, Key: key}
|
request := deleteRequest(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.appendFailed(request, start, err)
|
h.appendFailed(request, start, err)
|
||||||
return
|
return
|
||||||
@ -90,13 +94,13 @@ func (h *appendableHistory) AppendDelete(key string, start, end time.Time, resp
|
|||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: request,
|
Input: request,
|
||||||
Call: start.UnixNano(),
|
Call: start.UnixNano(),
|
||||||
Output: EtcdResponse{Revision: revision, Deleted: deleted, Err: err},
|
Output: deleteResponse(deleted, revision),
|
||||||
Return: end.UnixNano(),
|
Return: end.UnixNano(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *appendableHistory) AppendTxn(key, expectValue, newValue string, start, end time.Time, resp *clientv3.TxnResponse, err error) {
|
func (h *appendableHistory) AppendTxn(key, expectValue, newValue string, start, end time.Time, resp *clientv3.TxnResponse, err error) {
|
||||||
request := EtcdRequest{Op: Txn, Key: key, TxnExpectData: expectValue, TxnNewData: newValue}
|
request := txnRequest(key, expectValue, newValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.appendFailed(request, start, err)
|
h.appendFailed(request, start, err)
|
||||||
return
|
return
|
||||||
@ -109,7 +113,7 @@ func (h *appendableHistory) AppendTxn(key, expectValue, newValue string, start,
|
|||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: request,
|
Input: request,
|
||||||
Call: start.UnixNano(),
|
Call: start.UnixNano(),
|
||||||
Output: EtcdResponse{Err: err, Revision: revision, TxnSucceeded: resp.Succeeded},
|
Output: txnResponse(resp.Succeeded, revision),
|
||||||
Return: end.UnixNano(),
|
Return: end.UnixNano(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -119,7 +123,7 @@ func (h *appendableHistory) appendFailed(request EtcdRequest, start time.Time, e
|
|||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: request,
|
Input: request,
|
||||||
Call: start.UnixNano(),
|
Call: start.UnixNano(),
|
||||||
Output: EtcdResponse{Err: err},
|
Output: failedResponse(err),
|
||||||
Return: 0, // For failed writes we don't know when request has really finished.
|
Return: 0, // For failed writes we don't know when request has really finished.
|
||||||
})
|
})
|
||||||
// Operations of single client needs to be sequential.
|
// Operations of single client needs to be sequential.
|
||||||
@ -127,6 +131,46 @@ func (h *appendableHistory) appendFailed(request EtcdRequest, start time.Time, e
|
|||||||
h.id = h.idProvider.ClientId()
|
h.id = h.idProvider.ClientId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequest(key string) EtcdRequest {
|
||||||
|
return EtcdRequest{Ops: []EtcdOperation{{Type: Get, Key: key}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResponse(value string, revision int64) EtcdResponse {
|
||||||
|
return EtcdResponse{Result: []EtcdOperationResult{{Value: value}}, Revision: revision}
|
||||||
|
}
|
||||||
|
|
||||||
|
func failedResponse(err error) EtcdResponse {
|
||||||
|
return EtcdResponse{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func putRequest(key, value string) EtcdRequest {
|
||||||
|
return EtcdRequest{Ops: []EtcdOperation{{Type: Put, Key: key, Value: value}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func putResponse(revision int64) EtcdResponse {
|
||||||
|
return EtcdResponse{Result: []EtcdOperationResult{{}}, Revision: revision}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRequest(key string) EtcdRequest {
|
||||||
|
return EtcdRequest{Ops: []EtcdOperation{{Type: Delete, Key: key}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteResponse(deleted int64, revision int64) EtcdResponse {
|
||||||
|
return EtcdResponse{Result: []EtcdOperationResult{{Deleted: deleted}}, Revision: revision}
|
||||||
|
}
|
||||||
|
|
||||||
|
func txnRequest(key, expectValue, newValue string) EtcdRequest {
|
||||||
|
return EtcdRequest{Conds: []EtcdCondition{{Key: key, ExpectedValue: expectValue}}, Ops: []EtcdOperation{{Type: Put, Key: key, Value: newValue}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func txnResponse(succeeded bool, revision int64) EtcdResponse {
|
||||||
|
var result []EtcdOperationResult
|
||||||
|
if succeeded {
|
||||||
|
result = []EtcdOperationResult{{}}
|
||||||
|
}
|
||||||
|
return EtcdResponse{Result: result, TxnFailure: !succeeded, Revision: revision}
|
||||||
|
}
|
||||||
|
|
||||||
type history struct {
|
type history struct {
|
||||||
successful []porcupine.Operation
|
successful []porcupine.Operation
|
||||||
// failed requests are kept separate as we don't know return time of failed operations.
|
// failed requests are kept separate as we don't know return time of failed operations.
|
||||||
|
@ -17,33 +17,47 @@ package linearizability
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/anishathalye/porcupine"
|
"github.com/anishathalye/porcupine"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Operation string
|
type OperationType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Get Operation = "get"
|
Get OperationType = "get"
|
||||||
Put Operation = "put"
|
Put OperationType = "put"
|
||||||
Delete Operation = "delete"
|
Delete OperationType = "delete"
|
||||||
Txn Operation = "txn"
|
Txn OperationType = "txn"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EtcdRequest struct {
|
type EtcdRequest struct {
|
||||||
Op Operation
|
Conds []EtcdCondition
|
||||||
|
Ops []EtcdOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
type EtcdCondition struct {
|
||||||
Key string
|
Key string
|
||||||
PutData string
|
ExpectedValue string
|
||||||
TxnExpectData string
|
}
|
||||||
TxnNewData string
|
|
||||||
|
type EtcdOperation struct {
|
||||||
|
Type OperationType
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type EtcdResponse struct {
|
type EtcdResponse struct {
|
||||||
GetData string
|
Err error
|
||||||
Revision int64
|
Revision int64
|
||||||
Deleted int64
|
TxnFailure bool
|
||||||
TxnSucceeded bool
|
Result []EtcdOperationResult
|
||||||
Err error
|
}
|
||||||
|
|
||||||
|
type EtcdOperationResult struct {
|
||||||
|
Value string
|
||||||
|
Deleted int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type PossibleStates []EtcdState
|
type PossibleStates []EtcdState
|
||||||
@ -71,39 +85,83 @@ var etcdModel = porcupine.Model{
|
|||||||
return ok, string(data)
|
return ok, string(data)
|
||||||
},
|
},
|
||||||
DescribeOperation: func(in, out interface{}) string {
|
DescribeOperation: func(in, out interface{}) string {
|
||||||
request := in.(EtcdRequest)
|
return describeEtcdRequestResponse(in.(EtcdRequest), out.(EtcdResponse))
|
||||||
response := out.(EtcdResponse)
|
|
||||||
switch request.Op {
|
|
||||||
case Get:
|
|
||||||
if response.Err != nil {
|
|
||||||
return fmt.Sprintf("get(%q) -> %q", request.Key, response.Err)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("get(%q) -> %q, rev: %d", request.Key, response.GetData, response.Revision)
|
|
||||||
}
|
|
||||||
case Put:
|
|
||||||
if response.Err != nil {
|
|
||||||
return fmt.Sprintf("put(%q, %q) -> %s", request.Key, request.PutData, response.Err)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("put(%q, %q) -> ok, rev: %d", request.Key, request.PutData, response.Revision)
|
|
||||||
}
|
|
||||||
case Delete:
|
|
||||||
if response.Err != nil {
|
|
||||||
return fmt.Sprintf("delete(%q) -> %s", request.Key, response.Err)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("delete(%q) -> ok, rev: %d deleted:%d", request.Key, response.Revision, response.Deleted)
|
|
||||||
}
|
|
||||||
case Txn:
|
|
||||||
if response.Err != nil {
|
|
||||||
return fmt.Sprintf("txn(if(value(%q)=%q).then(put(%q, %q)) -> %s", request.Key, request.TxnExpectData, request.Key, request.TxnNewData, response.Err)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("txn(if(value(%q)=%q).then(put(%q, %q)) -> %v, rev: %d", request.Key, request.TxnExpectData, request.Key, request.TxnNewData, response.TxnSucceeded, response.Revision)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "<invalid>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func describeEtcdRequestResponse(request EtcdRequest, response EtcdResponse) string {
|
||||||
|
prefix := describeEtcdOperations(request.Ops)
|
||||||
|
if len(request.Conds) != 0 {
|
||||||
|
prefix = fmt.Sprintf("if(%s).then(%s)", describeEtcdConditions(request.Conds), prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s -> %s", prefix, describeEtcdResponse(request.Ops, response))
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeEtcdConditions(conds []EtcdCondition) string {
|
||||||
|
opsDescription := make([]string, len(conds))
|
||||||
|
for i := range conds {
|
||||||
|
opsDescription[i] = fmt.Sprintf("%s==%q", conds[i].Key, conds[i].ExpectedValue)
|
||||||
|
}
|
||||||
|
return strings.Join(opsDescription, " && ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeEtcdOperations(ops []EtcdOperation) string {
|
||||||
|
opsDescription := make([]string, len(ops))
|
||||||
|
for i := range ops {
|
||||||
|
opsDescription[i] = describeEtcdOperation(ops[i])
|
||||||
|
}
|
||||||
|
return strings.Join(opsDescription, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeEtcdResponse(ops []EtcdOperation, response EtcdResponse) string {
|
||||||
|
if response.Err != nil {
|
||||||
|
return fmt.Sprintf("err: %q", response.Err)
|
||||||
|
}
|
||||||
|
if response.TxnFailure {
|
||||||
|
return fmt.Sprintf("txn failed, rev: %d", response.Revision)
|
||||||
|
}
|
||||||
|
respDescription := make([]string, len(response.Result))
|
||||||
|
for i := range response.Result {
|
||||||
|
respDescription[i] = describeEtcdOperationResponse(ops[i].Type, response.Result[i])
|
||||||
|
}
|
||||||
|
respDescription = append(respDescription, fmt.Sprintf("rev: %d", response.Revision))
|
||||||
|
return strings.Join(respDescription, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeEtcdOperation(op EtcdOperation) string {
|
||||||
|
switch op.Type {
|
||||||
|
case Get:
|
||||||
|
return fmt.Sprintf("get(%q)", op.Key)
|
||||||
|
case Put:
|
||||||
|
return fmt.Sprintf("put(%q, %q)", op.Key, op.Value)
|
||||||
|
case Delete:
|
||||||
|
return fmt.Sprintf("delete(%q)", op.Key)
|
||||||
|
case Txn:
|
||||||
|
return "<! unsupported: nested transaction !>"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("<! unknown op: %q !>", op.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeEtcdOperationResponse(op OperationType, resp EtcdOperationResult) string {
|
||||||
|
switch op {
|
||||||
|
case Get:
|
||||||
|
if resp.Value == "" {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%q", resp.Value)
|
||||||
|
case Put:
|
||||||
|
return fmt.Sprintf("ok")
|
||||||
|
case Delete:
|
||||||
|
return fmt.Sprintf("deleted: %d", resp.Deleted)
|
||||||
|
case Txn:
|
||||||
|
return "<! unsupported: nested transaction !>"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("<! unknown op: %q !>", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func step(states PossibleStates, request EtcdRequest, response EtcdResponse) (bool, PossibleStates) {
|
func step(states PossibleStates, request EtcdRequest, response EtcdResponse) (bool, PossibleStates) {
|
||||||
if len(states) == 0 {
|
if len(states) == 0 {
|
||||||
// states were not initialized
|
// states were not initialized
|
||||||
@ -126,20 +184,22 @@ func initState(request EtcdRequest, response EtcdResponse) EtcdState {
|
|||||||
Revision: response.Revision,
|
Revision: response.Revision,
|
||||||
KeyValues: map[string]string{},
|
KeyValues: map[string]string{},
|
||||||
}
|
}
|
||||||
switch request.Op {
|
if response.TxnFailure {
|
||||||
case Get:
|
return state
|
||||||
if response.GetData != "" {
|
}
|
||||||
state.KeyValues[request.Key] = response.GetData
|
for i, op := range request.Ops {
|
||||||
|
opResp := response.Result[i]
|
||||||
|
switch op.Type {
|
||||||
|
case Get:
|
||||||
|
if opResp.Value != "" {
|
||||||
|
state.KeyValues[op.Key] = opResp.Value
|
||||||
|
}
|
||||||
|
case Put:
|
||||||
|
state.KeyValues[op.Key] = op.Value
|
||||||
|
case Delete:
|
||||||
|
default:
|
||||||
|
panic("Unknown operation")
|
||||||
}
|
}
|
||||||
case Put:
|
|
||||||
state.KeyValues[request.Key] = request.PutData
|
|
||||||
case Delete:
|
|
||||||
case Txn:
|
|
||||||
if response.TxnSucceeded {
|
|
||||||
state.KeyValues[request.Key] = request.TxnNewData
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("Unknown operation")
|
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -158,7 +218,7 @@ func applyRequest(states PossibleStates, request EtcdRequest, response EtcdRespo
|
|||||||
newStates := make(PossibleStates, 0, len(states))
|
newStates := make(PossibleStates, 0, len(states))
|
||||||
for _, s := range states {
|
for _, s := range states {
|
||||||
newState, expectResponse := applyRequestToSingleState(s, request)
|
newState, expectResponse := applyRequestToSingleState(s, request)
|
||||||
if expectResponse == response {
|
if reflect.DeepEqual(expectResponse, response) {
|
||||||
newStates = append(newStates, newState)
|
newStates = append(newStates, newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,33 +227,42 @@ func applyRequest(states PossibleStates, request EtcdRequest, response EtcdRespo
|
|||||||
|
|
||||||
// applyRequestToSingleState handles a successful request, returning updated state and response it would generate.
|
// applyRequestToSingleState handles a successful request, returning updated state and response it would generate.
|
||||||
func applyRequestToSingleState(s EtcdState, request EtcdRequest) (EtcdState, EtcdResponse) {
|
func applyRequestToSingleState(s EtcdState, request EtcdRequest) (EtcdState, EtcdResponse) {
|
||||||
|
success := true
|
||||||
|
for _, cond := range request.Conds {
|
||||||
|
if val := s.KeyValues[cond.Key]; val != cond.ExpectedValue {
|
||||||
|
success = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
return s, EtcdResponse{Revision: s.Revision, TxnFailure: true}
|
||||||
|
}
|
||||||
newKVs := map[string]string{}
|
newKVs := map[string]string{}
|
||||||
for k, v := range s.KeyValues {
|
for k, v := range s.KeyValues {
|
||||||
newKVs[k] = v
|
newKVs[k] = v
|
||||||
}
|
}
|
||||||
s.KeyValues = newKVs
|
s.KeyValues = newKVs
|
||||||
resp := EtcdResponse{}
|
opResp := make([]EtcdOperationResult, len(request.Ops))
|
||||||
switch request.Op {
|
increaseRevision := false
|
||||||
case Get:
|
for i, op := range request.Ops {
|
||||||
resp.GetData = s.KeyValues[request.Key]
|
switch op.Type {
|
||||||
case Put:
|
case Get:
|
||||||
s.KeyValues[request.Key] = request.PutData
|
opResp[i].Value = s.KeyValues[op.Key]
|
||||||
s.Revision += 1
|
case Put:
|
||||||
case Delete:
|
s.KeyValues[op.Key] = op.Value
|
||||||
if _, ok := s.KeyValues[request.Key]; ok {
|
increaseRevision = true
|
||||||
delete(s.KeyValues, request.Key)
|
case Delete:
|
||||||
s.Revision += 1
|
if _, ok := s.KeyValues[op.Key]; ok {
|
||||||
resp.Deleted = 1
|
delete(s.KeyValues, op.Key)
|
||||||
|
increaseRevision = true
|
||||||
|
opResp[i].Deleted = 1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unsupported operation")
|
||||||
}
|
}
|
||||||
case Txn:
|
|
||||||
if val := s.KeyValues[request.Key]; val == request.TxnExpectData {
|
|
||||||
s.KeyValues[request.Key] = request.TxnNewData
|
|
||||||
s.Revision += 1
|
|
||||||
resp.TxnSucceeded = true
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unsupported operation")
|
|
||||||
}
|
}
|
||||||
resp.Revision = s.Revision
|
if increaseRevision {
|
||||||
return s, resp
|
s.Revision += 1
|
||||||
|
}
|
||||||
|
return s, EtcdResponse{Result: opResp, Revision: s.Revision}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@ package linearizability
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestModel(t *testing.T) {
|
func TestModelStep(t *testing.T) {
|
||||||
tcs := []struct {
|
tcs := []struct {
|
||||||
name string
|
name string
|
||||||
operations []testOperation
|
operations []testOperation
|
||||||
@ -27,89 +29,89 @@ func TestModel(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "First Get can start from non-empty value and non-zero revision",
|
name: "First Get can start from non-empty value and non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 42}},
|
{req: getRequest("key"), resp: getResponse("", 42)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "First Put can start from non-zero revision",
|
name: "First Put can start from non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 42}},
|
{req: putRequest("key", "1"), resp: putResponse(42)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "First delete can start from non-zero revision",
|
name: "First delete can start from non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 42}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 42)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "First Txn can start from non-zero revision",
|
name: "First Txn can start from non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "", TxnNewData: "42"}, resp: EtcdResponse{Revision: 42}},
|
{req: txnRequest("key", "", "42"), resp: txnResponse(false, 42)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Get response data should match put",
|
name: "Get response data should match put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key1", PutData: "11"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key1", "11"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key2", PutData: "12"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key2", "12"), resp: putResponse(2)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "11", Revision: 1}, failure: true},
|
{req: getRequest("key1"), resp: getResponse("11", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "12", Revision: 1}, failure: true},
|
{req: getRequest("key1"), resp: getResponse("12", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "12", Revision: 2}, failure: true},
|
{req: getRequest("key1"), resp: getResponse("12", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "11", Revision: 2}},
|
{req: getRequest("key1"), resp: getResponse("11", 2)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "11", Revision: 2}, failure: true},
|
{req: getRequest("key2"), resp: getResponse("11", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "12", Revision: 1}, failure: true},
|
{req: getRequest("key2"), resp: getResponse("12", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "11", Revision: 1}, failure: true},
|
{req: getRequest("key2"), resp: getResponse("11", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "12", Revision: 2}},
|
{req: getRequest("key2"), resp: getResponse("12", 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put must increase revision by 1",
|
name: "Put must increase revision by 1",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}, failure: true},
|
{req: putRequest("key", "1"), resp: putResponse(1), failure: true},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 3}, failure: true},
|
{req: putRequest("key", "1"), resp: putResponse(3), failure: true},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key", "1"), resp: putResponse(2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail and be lost before get",
|
name: "Put can fail and be lost before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail and be lost before put",
|
name: "Put can fail and be lost before put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key", "3"), resp: getResponse("", 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail and be lost before delete",
|
name: "Put can fail and be lost before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail and be lost before txn failed",
|
name: "Put can fail and be lost before txn",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// Txn failure
|
// Txn failure
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1}},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 1)},
|
||||||
// Txn success
|
// Txn success
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key", "2"), resp: putResponse(2)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
{req: txnRequest("key", "2", "5"), resp: txnResponse(true, 3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -120,284 +122,284 @@ func TestModel(t *testing.T) {
|
|||||||
name: "Put can fail but be persisted and increase revision before get",
|
name: "Put can fail but be persisted and increase revision before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("3", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
||||||
// Two failed request, two persisted.
|
// Two failed request, two persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "3"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "4", Revision: 4}},
|
{req: getRequest("key"), resp: getResponse("4", 4)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail but be persisted and increase revision before delete",
|
name: "Put can fail but be persisted and increase revision before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 1}, failure: true},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 3)},
|
||||||
// Two failed request, two persisted.
|
// Two failed request, two persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "6"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 7}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 7)},
|
||||||
// Two failed request, one persisted.
|
// Two failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "8"}, resp: EtcdResponse{Revision: 8}},
|
{req: putRequest("key", "8"), resp: putResponse(8)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "9"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "9"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "10"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "10"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 10}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 10)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Put can fail but be persisted before txn",
|
name: "Put can fail but be persisted before txn",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// Txn success
|
// Txn success
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 2}, failure: true},
|
{req: txnRequest("key", "2", ""), resp: txnResponse(true, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
{req: txnRequest("key", "2", ""), resp: txnResponse(true, 3)},
|
||||||
// Txn failure
|
// Txn failure
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5"}, resp: EtcdResponse{Revision: 4}},
|
{req: txnRequest("key", "5", ""), resp: txnResponse(false, 4)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 5, GetData: "5"}},
|
{req: getRequest("key"), resp: getResponse("5", 5)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete only increases revision on success",
|
name: "Delete only increases revision on success",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key1", PutData: "11"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key1", "11"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key2", PutData: "12"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key2", "12"), resp: putResponse(2)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
{req: deleteRequest("key1"), resp: deleteResponse(1, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
{req: deleteRequest("key1"), resp: deleteResponse(1, 3)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 0, Revision: 4}, failure: true},
|
{req: deleteRequest("key1"), resp: deleteResponse(0, 4), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 0, Revision: 3}},
|
{req: deleteRequest("key1"), resp: deleteResponse(0, 3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete not existing key",
|
name: "Delete not existing key",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 0, Revision: 1}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete clears value",
|
name: "Delete clears value",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("1", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
{req: getRequest("key"), resp: getResponse("", 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail and be lost before get",
|
name: "Delete can fail and be lost before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("", 2), failure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail and be lost before delete",
|
name: "Delete can fail and be lost before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 1}, failure: true},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail and be lost before put",
|
name: "Delete can fail and be lost before put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key", "1"), resp: putResponse(2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail but be persisted before get",
|
name: "Delete can fail but be persisted before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
{req: getRequest("key"), resp: getResponse("", 2)},
|
||||||
// Two failed request, one persisted.
|
// Two failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 4}},
|
{req: getRequest("key"), resp: getResponse("", 4)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail but be persisted before put",
|
name: "Delete can fail but be persisted before put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
||||||
// Two failed request, one persisted.
|
// Two failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Revision: 5}},
|
{req: putRequest("key", "5"), resp: putResponse(5)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail but be persisted before delete",
|
name: "Delete can fail but be persisted before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 2)},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
||||||
// Two failed request, one persisted.
|
// Two failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 4}},
|
{req: deleteRequest("key"), resp: deleteResponse(0, 4)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delete can fail but be persisted before txn",
|
name: "Delete can fail but be persisted before txn",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// Txn success
|
// Txn success
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "", TxnNewData: "1"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
{req: txnRequest("key", "", "3"), resp: txnResponse(true, 3)},
|
||||||
// Txn failure
|
// Txn failure
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{TxnSucceeded: false, Revision: 5}},
|
{req: txnRequest("key", "4", "5"), resp: txnResponse(false, 5)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn sets new value if value matches expected",
|
name: "Txn sets new value if value matches expected",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: true}, failure: true},
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(true, 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: false}, failure: true},
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(false, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: false}, failure: true},
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(false, 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}},
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(true, 2)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("1", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can expect on empty key",
|
name: "Txn can expect on empty key",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{Revision: 1}},
|
{req: getRequest("key1"), resp: getResponse("", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key1", TxnExpectData: "", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}},
|
{req: txnRequest("key1", "", "2"), resp: txnResponse(true, 2)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key2", TxnExpectData: "", TxnNewData: "3"}, resp: EtcdResponse{Revision: 3, TxnSucceeded: true}},
|
{req: txnRequest("key2", "", "3"), resp: txnResponse(true, 3)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key3", TxnExpectData: "4", TxnNewData: "4"}, resp: EtcdResponse{Revision: 4}, failure: true},
|
{req: txnRequest("key3", "4", "4"), resp: txnResponse(false, 4), failure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn doesn't do anything if value doesn't match expected",
|
name: "Txn doesn't do anything if value doesn't match expected",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}, failure: true},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: true}, failure: true},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: false}, failure: true},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: false}},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 1)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 1}, failure: true},
|
{req: getRequest("key"), resp: getResponse("3", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 2}, failure: true},
|
{req: getRequest("key"), resp: getResponse("3", 2), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail and be lost before get",
|
name: "Txn can fail and be lost before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2, GetData: "2"}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail and be lost before delete",
|
name: "Txn can fail and be lost before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail and be lost before put",
|
name: "Txn can fail and be lost before put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 2}},
|
{req: putRequest("key", "3"), resp: putResponse(2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail but be persisted before get",
|
name: "Txn can fail but be persisted before get",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1, GetData: "2"}, failure: true},
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2, GetData: "2"}},
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
||||||
// Two failed request, two persisted.
|
// Two failed request, two persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "3", TxnNewData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "3", "4"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 5, GetData: "5"}},
|
{req: getRequest("key"), resp: getResponse("5", 5)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail but be persisted before put",
|
name: "Txn can fail but be persisted before put",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
||||||
// Two failed request, two persisted.
|
// Two failed request, two persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "7"}, resp: EtcdResponse{Revision: 7}},
|
{req: putRequest("key", "7"), resp: putResponse(7)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail but be persisted before delete",
|
name: "Txn can fail but be persisted before delete",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted.
|
// One failed request, one persisted.
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 3)},
|
||||||
// Two failed request, two persisted.
|
// Two failed request, two persisted.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 7}},
|
{req: deleteRequest("key"), resp: deleteResponse(1, 7)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Txn can fail but be persisted before txn",
|
name: "Txn can fail but be persisted before txn",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
// One failed request, one persisted with success.
|
// One failed request, one persisted with success.
|
||||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 3, TxnSucceeded: true}},
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 3)},
|
||||||
// Two failed request, two persisted with success.
|
// Two failed request, two persisted with success.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "6", TxnNewData: "7"}, resp: EtcdResponse{Revision: 7, TxnSucceeded: true}},
|
{req: txnRequest("key", "6", "7"), resp: txnResponse(true, 7)},
|
||||||
// One failed request, one persisted with failure.
|
// One failed request, one persisted with failure.
|
||||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "8"}, resp: EtcdResponse{Revision: 8}},
|
{req: putRequest("key", "8"), resp: putResponse(8)},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "8", TxnNewData: "9"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
{req: txnRequest("key", "8", "9"), resp: failedResponse(errors.New("failed"))},
|
||||||
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "8", TxnNewData: "10"}, resp: EtcdResponse{Revision: 9}},
|
{req: txnRequest("key", "8", "10"), resp: txnResponse(false, 9)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -424,3 +426,60 @@ type testOperation struct {
|
|||||||
resp EtcdResponse
|
resp EtcdResponse
|
||||||
failure bool
|
failure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModelDescribe(t *testing.T) {
|
||||||
|
tcs := []struct {
|
||||||
|
req EtcdRequest
|
||||||
|
resp EtcdResponse
|
||||||
|
expectDescribe string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
req: getRequest("key1"),
|
||||||
|
resp: getResponse("", 1),
|
||||||
|
expectDescribe: `get("key1") -> nil, rev: 1`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: getRequest("key2"),
|
||||||
|
resp: getResponse("2", 2),
|
||||||
|
expectDescribe: `get("key2") -> "2", rev: 2`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: putRequest("key3", "3"),
|
||||||
|
resp: putResponse(3),
|
||||||
|
expectDescribe: `put("key3", "3") -> ok, rev: 3`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: putRequest("key4", "4"),
|
||||||
|
resp: failedResponse(errors.New("failed")),
|
||||||
|
expectDescribe: `put("key4", "4") -> err: "failed"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: deleteRequest("key5"),
|
||||||
|
resp: deleteResponse(1, 5),
|
||||||
|
expectDescribe: `delete("key5") -> deleted: 1, rev: 5`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: deleteRequest("key6"),
|
||||||
|
resp: failedResponse(errors.New("failed")),
|
||||||
|
expectDescribe: `delete("key6") -> err: "failed"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: txnRequest("key7", "7", "77"),
|
||||||
|
resp: txnResponse(false, 7),
|
||||||
|
expectDescribe: `if(key7=="7").then(put("key7", "77")) -> txn failed, rev: 7`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: txnRequest("key8", "8", "88"),
|
||||||
|
resp: txnResponse(true, 8),
|
||||||
|
expectDescribe: `if(key8=="8").then(put("key8", "88")) -> ok, rev: 8`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: txnRequest("key9", "9", "99"),
|
||||||
|
resp: failedResponse(errors.New("failed")),
|
||||||
|
expectDescribe: `if(key9=="9").then(put("key9", "99")) -> err: "failed"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tcs {
|
||||||
|
assert.Equal(t, tc.expectDescribe, etcdModel.DescribeOperation(tc.req, tc.resp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -39,7 +39,7 @@ type readWriteSingleKey struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type opChance struct {
|
type opChance struct {
|
||||||
operation Operation
|
operation OperationType
|
||||||
chance int
|
chance int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ func (t readWriteSingleKey) Write(ctx context.Context, c *recordingClient, limit
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t readWriteSingleKey) pickWriteOperation() Operation {
|
func (t readWriteSingleKey) pickWriteOperation() OperationType {
|
||||||
sum := 0
|
sum := 0
|
||||||
for _, op := range t.writes {
|
for _, op := range t.writes {
|
||||||
sum += op.chance
|
sum += op.chance
|
||||||
|
Loading…
x
Reference in New Issue
Block a user