mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
tests: Make etcdRequest and etcdResponse public to make it serializable
Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
This commit is contained in:
parent
68fd863c87
commit
b922afc0a3
@ -70,9 +70,9 @@ func (c *recordingClient) Get(ctx context.Context, key string) error {
|
||||
}
|
||||
c.operations = append(c.operations, porcupine.Operation{
|
||||
ClientId: c.id,
|
||||
Input: etcdRequest{op: Get, key: key},
|
||||
Input: EtcdRequest{Op: Get, Key: key},
|
||||
Call: callTime.UnixNano(),
|
||||
Output: etcdResponse{getData: readData, revision: resp.Header.Revision},
|
||||
Output: EtcdResponse{GetData: readData, Revision: resp.Header.Revision},
|
||||
Return: returnTime.UnixNano(),
|
||||
})
|
||||
return nil
|
||||
@ -85,9 +85,9 @@ func (c *recordingClient) Put(ctx context.Context, key, value string) error {
|
||||
if err != nil {
|
||||
c.failed = append(c.failed, porcupine.Operation{
|
||||
ClientId: c.id,
|
||||
Input: etcdRequest{op: Put, key: key, putData: value},
|
||||
Input: EtcdRequest{Op: Put, Key: key, PutData: value},
|
||||
Call: callTime.UnixNano(),
|
||||
Output: etcdResponse{err: err},
|
||||
Output: EtcdResponse{Err: err},
|
||||
Return: 0, // For failed writes we don't know when request has really finished.
|
||||
})
|
||||
// Operations of single client needs to be sequential.
|
||||
@ -101,9 +101,9 @@ func (c *recordingClient) Put(ctx context.Context, key, value string) error {
|
||||
}
|
||||
c.operations = append(c.operations, porcupine.Operation{
|
||||
ClientId: c.id,
|
||||
Input: etcdRequest{op: Put, key: key, putData: value},
|
||||
Input: EtcdRequest{Op: Put, Key: key, PutData: value},
|
||||
Call: callTime.UnixNano(),
|
||||
Output: etcdResponse{err: err, revision: revision},
|
||||
Output: EtcdResponse{Err: err, Revision: revision},
|
||||
Return: returnTime.UnixNano(),
|
||||
})
|
||||
return nil
|
||||
@ -116,9 +116,9 @@ func (c *recordingClient) Delete(ctx context.Context, key string) error {
|
||||
if err != nil {
|
||||
c.failed = append(c.failed, porcupine.Operation{
|
||||
ClientId: c.id,
|
||||
Input: etcdRequest{op: Delete, key: key},
|
||||
Input: EtcdRequest{Op: Delete, Key: key},
|
||||
Call: callTime.UnixNano(),
|
||||
Output: etcdResponse{err: err},
|
||||
Output: EtcdResponse{Err: err},
|
||||
Return: 0, // For failed writes we don't know when request has really finished.
|
||||
})
|
||||
// Operations of single client needs to be sequential.
|
||||
@ -134,9 +134,9 @@ func (c *recordingClient) Delete(ctx context.Context, key string) error {
|
||||
}
|
||||
c.operations = append(c.operations, porcupine.Operation{
|
||||
ClientId: c.id,
|
||||
Input: etcdRequest{op: Delete, key: key},
|
||||
Input: EtcdRequest{Op: Delete, Key: key},
|
||||
Call: callTime.UnixNano(),
|
||||
Output: etcdResponse{revision: revision, deleted: deleted, err: err},
|
||||
Output: EtcdResponse{Revision: revision, Deleted: deleted, Err: err},
|
||||
Return: returnTime.UnixNano(),
|
||||
})
|
||||
return nil
|
||||
|
@ -29,17 +29,17 @@ const (
|
||||
Delete Operation = "delete"
|
||||
)
|
||||
|
||||
type etcdRequest struct {
|
||||
op Operation
|
||||
key string
|
||||
putData string
|
||||
type EtcdRequest struct {
|
||||
Op Operation
|
||||
Key string
|
||||
PutData string
|
||||
}
|
||||
|
||||
type etcdResponse struct {
|
||||
getData string
|
||||
revision int64
|
||||
deleted int64
|
||||
err error
|
||||
type EtcdResponse struct {
|
||||
GetData string
|
||||
Revision int64
|
||||
Deleted int64
|
||||
Err error
|
||||
}
|
||||
|
||||
type EtcdState struct {
|
||||
@ -57,7 +57,7 @@ var etcdModel = porcupine.Model{
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ok, state := step(state, in.(etcdRequest), out.(etcdResponse))
|
||||
ok, state := step(state, in.(EtcdRequest), out.(EtcdResponse))
|
||||
data, err := json.Marshal(state)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -65,26 +65,26 @@ var etcdModel = porcupine.Model{
|
||||
return ok, string(data)
|
||||
},
|
||||
DescribeOperation: func(in, out interface{}) string {
|
||||
request := in.(etcdRequest)
|
||||
response := out.(etcdResponse)
|
||||
switch request.op {
|
||||
request := in.(EtcdRequest)
|
||||
response := out.(EtcdResponse)
|
||||
switch request.Op {
|
||||
case Get:
|
||||
if response.err != nil {
|
||||
return fmt.Sprintf("get(%q) -> %q", request.key, response.err)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return fmt.Sprintf("delete(%q) -> ok, rev: %d deleted:%d", request.Key, response.Revision, response.Deleted)
|
||||
}
|
||||
default:
|
||||
return "<invalid>"
|
||||
@ -92,17 +92,17 @@ var etcdModel = porcupine.Model{
|
||||
},
|
||||
}
|
||||
|
||||
func step(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
|
||||
if request.key == "" {
|
||||
func step(state EtcdState, request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
|
||||
if request.Key == "" {
|
||||
panic("invalid request")
|
||||
}
|
||||
if state.Key == "" {
|
||||
return true, initState(request, response)
|
||||
}
|
||||
if state.Key != request.key {
|
||||
if state.Key != request.Key {
|
||||
panic("Multiple keys not supported")
|
||||
}
|
||||
switch request.op {
|
||||
switch request.Op {
|
||||
case Get:
|
||||
return stepGet(state, request, response)
|
||||
case Put:
|
||||
@ -114,23 +114,23 @@ func step(state EtcdState, request etcdRequest, response etcdResponse) (bool, Et
|
||||
}
|
||||
}
|
||||
|
||||
func initState(request etcdRequest, response etcdResponse) EtcdState {
|
||||
func initState(request EtcdRequest, response EtcdResponse) EtcdState {
|
||||
state := EtcdState{
|
||||
Key: request.key,
|
||||
LastRevision: response.revision,
|
||||
Key: request.Key,
|
||||
LastRevision: response.Revision,
|
||||
FailedWrites: map[string]struct{}{},
|
||||
}
|
||||
switch request.op {
|
||||
switch request.Op {
|
||||
case Get:
|
||||
state.Value = response.getData
|
||||
state.Value = response.GetData
|
||||
case Put:
|
||||
if response.err == nil {
|
||||
state.Value = request.putData
|
||||
if response.Err == nil {
|
||||
state.Value = request.PutData
|
||||
} else {
|
||||
state.FailedWrites[request.putData] = struct{}{}
|
||||
state.FailedWrites[request.PutData] = struct{}{}
|
||||
}
|
||||
case Delete:
|
||||
if response.err != nil {
|
||||
if response.Err != nil {
|
||||
state.FailedWrites[""] = struct{}{}
|
||||
}
|
||||
default:
|
||||
@ -139,39 +139,39 @@ func initState(request etcdRequest, response etcdResponse) EtcdState {
|
||||
return state
|
||||
}
|
||||
|
||||
func stepGet(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
|
||||
if state.Value == response.getData && state.LastRevision <= response.revision {
|
||||
func stepGet(state EtcdState, request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
|
||||
if state.Value == response.GetData && state.LastRevision <= response.Revision {
|
||||
return true, state
|
||||
}
|
||||
_, ok := state.FailedWrites[response.getData]
|
||||
if ok && state.LastRevision < response.revision {
|
||||
state.Value = response.getData
|
||||
state.LastRevision = response.revision
|
||||
delete(state.FailedWrites, response.getData)
|
||||
_, ok := state.FailedWrites[response.GetData]
|
||||
if ok && state.LastRevision < response.Revision {
|
||||
state.Value = response.GetData
|
||||
state.LastRevision = response.Revision
|
||||
delete(state.FailedWrites, response.GetData)
|
||||
return true, state
|
||||
}
|
||||
return false, state
|
||||
}
|
||||
|
||||
func stepPut(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
|
||||
if response.err != nil {
|
||||
state.FailedWrites[request.putData] = struct{}{}
|
||||
func stepPut(state EtcdState, request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
|
||||
if response.Err != nil {
|
||||
state.FailedWrites[request.PutData] = struct{}{}
|
||||
return true, state
|
||||
}
|
||||
if state.LastRevision >= response.revision {
|
||||
if state.LastRevision >= response.Revision {
|
||||
return false, state
|
||||
}
|
||||
state.Value = request.putData
|
||||
state.LastRevision = response.revision
|
||||
state.Value = request.PutData
|
||||
state.LastRevision = response.Revision
|
||||
return true, state
|
||||
}
|
||||
|
||||
func stepDelete(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
|
||||
if response.err != nil {
|
||||
func stepDelete(state EtcdState, request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
|
||||
if response.Err != nil {
|
||||
state.FailedWrites[""] = struct{}{}
|
||||
return true, state
|
||||
}
|
||||
deleteSucceeded := response.deleted != 0
|
||||
deleteSucceeded := response.Deleted != 0
|
||||
keySet := state.Value != ""
|
||||
|
||||
//non-existent key cannot be deleted.
|
||||
@ -179,15 +179,15 @@ func stepDelete(state EtcdState, request etcdRequest, response etcdResponse) (bo
|
||||
return false, state
|
||||
}
|
||||
//if key was deleted, response revision should go up
|
||||
if deleteSucceeded && state.LastRevision >= response.revision {
|
||||
if deleteSucceeded && state.LastRevision >= response.Revision {
|
||||
return false, state
|
||||
}
|
||||
//if key was not deleted, response revision should not change
|
||||
if !deleteSucceeded && state.LastRevision != response.revision {
|
||||
if !deleteSucceeded && state.LastRevision != response.Revision {
|
||||
return false, state
|
||||
}
|
||||
|
||||
state.Value = ""
|
||||
state.LastRevision = response.revision
|
||||
state.LastRevision = response.Revision
|
||||
return true, state
|
||||
}
|
||||
|
@ -27,91 +27,91 @@ 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: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 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: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 42}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Get response data should match PUT",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{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: 1}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{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: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Get response revision should be equal or greater then put",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key"}, resp: etcdResponse{revision: 2}},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{revision: 1}, failure: true},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{revision: 2}},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{revision: 4}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 1}, failure: true},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 2}},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{Revision: 4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put bumps revision",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{revision: 1}},
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "2"}, resp: etcdResponse{revision: 1}, failure: true},
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "2"}, resp: etcdResponse{revision: 2}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 1}, failure: true},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put can fail and be lost",
|
||||
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: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 2}},
|
||||
{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: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 2}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put can fail but bump revision",
|
||||
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: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 3}},
|
||||
{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: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 3}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put can fail but be persisted and bump revision",
|
||||
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: "2", revision: 1}, failure: true},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{getData: "2", revision: 2}},
|
||||
{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: "2", Revision: 1}, failure: true},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put can fail but be persisted later",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{err: errors.New("failed")}},
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "2"}, resp: etcdResponse{revision: 2}},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{getData: "2", revision: 2}},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{getData: "1", revision: 3}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "1", Revision: 3}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Put can fail but bump revision later",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{err: errors.New("failed")}},
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "2"}, resp: etcdResponse{revision: 2}},
|
||||
{req: etcdRequest{op: Get, key: "key"}, resp: etcdResponse{getData: "2", revision: 2}},
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 4}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Err: errors.New("failed")}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "2"}, resp: EtcdResponse{Revision: 2}},
|
||||
{req: EtcdRequest{Op: Get, Key: "key"}, resp: EtcdResponse{GetData: "2", Revision: 2}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "3"}, resp: EtcdResponse{Revision: 4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Delete only increases revision on success",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{revision: 1}},
|
||||
{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: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 0, revision: 3}, failure: true},
|
||||
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 0, revision: 2}},
|
||||
{req: EtcdRequest{Op: Put, Key: "key", PutData: "1"}, resp: EtcdResponse{Revision: 1}},
|
||||
{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: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 0, Revision: 3}, failure: true},
|
||||
{req: EtcdRequest{Op: Delete, Key: "key"}, resp: EtcdResponse{Deleted: 0, Revision: 2}},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -131,7 +131,7 @@ func TestModel(t *testing.T) {
|
||||
}
|
||||
|
||||
type testOperation struct {
|
||||
req etcdRequest
|
||||
resp etcdResponse
|
||||
req EtcdRequest
|
||||
resp EtcdResponse
|
||||
failure bool
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user