|
|
|
@ -17,9 +17,11 @@ package linearizability
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestModel(t *testing.T) {
|
|
|
|
|
func TestModelStep(t *testing.T) {
|
|
|
|
|
tcs := []struct {
|
|
|
|
|
name string
|
|
|
|
|
operations []testOperation
|
|
|
|
@ -27,89 +29,89 @@ func TestModel(t *testing.T) {
|
|
|
|
|
{
|
|
|
|
|
name: "First Get can start from non-empty value and non-zero revision",
|
|
|
|
|
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",
|
|
|
|
|
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",
|
|
|
|
|
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",
|
|
|
|
|
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",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key1", PutData: "11"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key2", PutData: "12"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "11", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "12", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "12", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{GetData: "11", Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "11", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "12", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "11", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key2"}, resp: EtcdResponse{GetData: "12", Revision: 2}},
|
|
|
|
|
{req: putRequest("key1", "11"), resp: putResponse(1)},
|
|
|
|
|
{req: putRequest("key2", "12"), resp: putResponse(2)},
|
|
|
|
|
{req: getRequest("key1"), resp: getResponse("11", 1), failure: true},
|
|
|
|
|
{req: getRequest("key1"), resp: getResponse("12", 1), failure: true},
|
|
|
|
|
{req: getRequest("key1"), resp: getResponse("12", 2), failure: true},
|
|
|
|
|
{req: getRequest("key1"), resp: getResponse("11", 2)},
|
|
|
|
|
{req: getRequest("key2"), resp: getResponse("11", 2), failure: true},
|
|
|
|
|
{req: getRequest("key2"), resp: getResponse("12", 1), failure: true},
|
|
|
|
|
{req: getRequest("key2"), resp: getResponse("11", 1), failure: true},
|
|
|
|
|
{req: getRequest("key2"), resp: getResponse("12", 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put must increase revision by 1",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 3}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1), failure: true},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(3), failure: true},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put can fail and be lost before get",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}, failure: true},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put can fail and be lost before put",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "3"), resp: getResponse("", 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put can fail and be lost before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{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{
|
|
|
|
|
// Txn failure
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 1)},
|
|
|
|
|
// Txn success
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
|
|
|
|
{req: putRequest("key", "2"), resp: putResponse(2)},
|
|
|
|
|
{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{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",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("3", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
|
|
|
|
// Two failed request, two persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "4", Revision: 4}},
|
|
|
|
|
{req: putRequest("key", "3"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("4", 4)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put can fail but be persisted and increase revision before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
|
|
|
|
{req: putRequest("key", "1"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 1), failure: true},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2), failure: true},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 3)},
|
|
|
|
|
// Two failed request, two persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 7}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "6"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 7)},
|
|
|
|
|
// Two failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "8"}, resp: EtcdResponse{Revision: 8}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "9"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "10"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 10}},
|
|
|
|
|
{req: putRequest("key", "8"), resp: putResponse(8)},
|
|
|
|
|
{req: putRequest("key", "9"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "10"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 10)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Put can fail but be persisted before txn",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// Txn success
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
|
|
|
|
{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "2", ""), resp: txnResponse(true, 2), failure: true},
|
|
|
|
|
{req: txnRequest("key", "2", ""), resp: txnResponse(true, 3)},
|
|
|
|
|
// Txn failure
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 5, GetData: "5"}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: txnRequest("key", "5", ""), resp: txnResponse(false, 4)},
|
|
|
|
|
{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("5", 5)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete only increases revision on success",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key1", PutData: "11"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key2", PutData: "12"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 0, Revision: 4}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key1"}, resp: EtcdResponse{Deleted: 0, Revision: 3}},
|
|
|
|
|
{req: putRequest("key1", "11"), resp: putResponse(1)},
|
|
|
|
|
{req: putRequest("key2", "12"), resp: putResponse(2)},
|
|
|
|
|
{req: deleteRequest("key1"), resp: deleteResponse(1, 2), failure: true},
|
|
|
|
|
{req: deleteRequest("key1"), resp: deleteResponse(1, 3)},
|
|
|
|
|
{req: deleteRequest("key1"), resp: deleteResponse(0, 4), failure: true},
|
|
|
|
|
{req: deleteRequest("key1"), resp: deleteResponse(0, 3)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete not existing key",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 0, Revision: 1}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2), failure: true},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(0, 1)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete clears value",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail and be lost before get",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}, failure: true},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 2), failure: true},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail and be lost before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 1), failure: true},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail and be lost before put",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail but be persisted before get",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 2)},
|
|
|
|
|
// Two failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("", 4)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail but be persisted before put",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
|
|
|
|
// Two failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "5"}, resp: EtcdResponse{Revision: 5}},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "5"), resp: putResponse(5)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail but be persisted before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
|
|
|
|
{req: putRequest("key", "1"), resp: putResponse(1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(0, 2)},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
|
|
|
|
// Two failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(0, 4)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Delete can fail but be persisted before txn",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// Txn success
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "", TxnNewData: "1"}, resp: EtcdResponse{TxnSucceeded: true, Revision: 3}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "", "3"), resp: txnResponse(true, 3)},
|
|
|
|
|
// Txn failure
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{TxnSucceeded: false, Revision: 5}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: deleteRequest("key"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "4", "5"), resp: txnResponse(false, 5)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn sets new value if value matches expected",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: true}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: false}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: false}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(true, 1), failure: true},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(false, 2), failure: true},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(false, 1), failure: true},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: txnResponse(true, 2)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can expect on empty key",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key1"}, resp: EtcdResponse{Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key1", TxnExpectData: "", TxnNewData: "2"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key2", TxnExpectData: "", TxnNewData: "3"}, resp: EtcdResponse{Revision: 3, TxnSucceeded: true}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key3", TxnExpectData: "4", TxnNewData: "4"}, resp: EtcdResponse{Revision: 4}, failure: true},
|
|
|
|
|
{req: getRequest("key1"), resp: getResponse("", 1)},
|
|
|
|
|
{req: txnRequest("key1", "", "2"), resp: txnResponse(true, 2)},
|
|
|
|
|
{req: txnRequest("key2", "", "3"), resp: txnResponse(true, 3)},
|
|
|
|
|
{req: txnRequest("key3", "4", "4"), resp: txnResponse(false, 4), failure: true},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn doesn't do anything if value doesn't match expected",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: true}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: true}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 2, TxnSucceeded: false}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 1, TxnSucceeded: false}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 1}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "3", Revision: 2}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 2), failure: true},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 1), failure: true},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 2), failure: true},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(false, 1)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("3", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("3", 2), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail and be lost before get",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2, GetData: "2"}, failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2), failure: true},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail and be lost before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail and be lost before put",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 2}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(2)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail but be persisted before get",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1, GetData: "2"}, failure: true},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2, GetData: "2"}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 1), failure: true},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("2", 2)},
|
|
|
|
|
// Two failed request, two persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "3", TxnNewData: "4"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 5, GetData: "5"}},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
|
|
|
|
{req: txnRequest("key", "3", "4"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("5", 5)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail but be persisted before put",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "3"), resp: putResponse(3)},
|
|
|
|
|
// Two failed request, two persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "7"}, resp: EtcdResponse{Revision: 7}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: putRequest("key", "7"), resp: putResponse(7)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail but be persisted before delete",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 3}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 3)},
|
|
|
|
|
// Two failed request, two persisted.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 1, Revision: 7}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: deleteRequest("key"), resp: deleteResponse(1, 7)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "Txn can fail but be persisted before txn",
|
|
|
|
|
operations: []testOperation{
|
|
|
|
|
// One failed request, one persisted with success.
|
|
|
|
|
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 1}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "1", TxnNewData: "2"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "2", TxnNewData: "3"}, resp: EtcdResponse{Revision: 3, TxnSucceeded: true}},
|
|
|
|
|
{req: getRequest("key"), resp: getResponse("1", 1)},
|
|
|
|
|
{req: txnRequest("key", "1", "2"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "2", "3"), resp: txnResponse(true, 3)},
|
|
|
|
|
// Two failed request, two persisted with success.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "4"}, resp: EtcdResponse{Revision: 4}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "4", TxnNewData: "5"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "5", TxnNewData: "6"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "6", TxnNewData: "7"}, resp: EtcdResponse{Revision: 7, TxnSucceeded: true}},
|
|
|
|
|
{req: putRequest("key", "4"), resp: putResponse(4)},
|
|
|
|
|
{req: txnRequest("key", "4", "5"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "5", "6"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "6", "7"), resp: txnResponse(true, 7)},
|
|
|
|
|
// One failed request, one persisted with failure.
|
|
|
|
|
{req: EtcdRequest{Op: Put, Key: "key", PutData: "8"}, resp: EtcdResponse{Revision: 8}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "8", TxnNewData: "9"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
|
|
|
|
{req: EtcdRequest{Op: Txn, Key: "key", TxnExpectData: "8", TxnNewData: "10"}, resp: EtcdResponse{Revision: 9}},
|
|
|
|
|
{req: putRequest("key", "8"), resp: putResponse(8)},
|
|
|
|
|
{req: txnRequest("key", "8", "9"), resp: failedResponse(errors.New("failed"))},
|
|
|
|
|
{req: txnRequest("key", "8", "10"), resp: txnResponse(false, 9)},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
@ -424,3 +426,60 @@ type testOperation struct {
|
|
|
|
|
resp EtcdResponse
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|