package watch
import (
"context"
"net/http"
"testing"
"github.com/coreos/etcd/clientv3"
v3rpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
"github.com/coreos/etcd/mvcc/mvccpb"
)
func TestParseWatchResourceVersion(t *testing.T) {
if got, err := ParseWatchResourceVersion(""); err != nil || got != 0 {
t.Fatalf("expected empty resource version to map to 0, got %d err=%v", got, err)
}
if got, err := ParseWatchResourceVersion("0"); err != nil || got != 0 {
t.Fatalf("expected zero resource version to map to 0, got %d err=%v", got, err)
}
if got, err := ParseWatchResourceVersion("123"); err != nil || got != 123 {
t.Fatalf("expected 123, got %d err=%v", got, err)
}
if _, err := ParseWatchResourceVersion("bad"); err == nil {
t.Fatal("expected parse error for invalid resource version")
}
}
func TestEventAccessors(t *testing.T) {
e := Event{
Type: Modified,
Source: &event{
key: "/demo/key",
value: []byte("new"),
prevValue: []byte("old"),
},
}
if e.GetKey() != "/demo/key" {
t.Fatalf("unexpected key: %q", e.GetKey())
}
if e.GetValueString() != "new" || e.GetPreValueString() != "old" {
t.Fatalf("unexpected values: new=%q old=%q", e.GetValueString(), e.GetPreValueString())
}
}
func TestEventByteAccessors(t *testing.T) {
e := Event{
Source: &event{
value: []byte("current"),
prevValue: []byte("previous"),
},
}
if string(e.GetValue()) != "current" || string(e.GetPreValue()) != "previous" {
t.Fatalf("unexpected byte values: current=%q previous=%q", string(e.GetValue()), string(e.GetPreValue()))
}
}
func TestStatusError(t *testing.T) {
s := Status{Code: 500, Status: "Failure", Message: "boom", Reason: "Test"}
if got := s.Error(); got != "(500)Status:Failure Message:boom Reason:Test" {
t.Fatalf("unexpected status error string: %q", got)
}
}
func TestParseKVMarksCreateEvent(t *testing.T) {
kv := &mvccpb.KeyValue{
Key: []byte("/demo/key"),
Value: []byte("value"),
ModRevision: 9,
}
e := parseKV(kv)
if e == nil || !e.isCreated || e.isDeleted || e.rev != 9 {
t.Fatalf("unexpected parsed kv event: %+v", e)
}
}
func TestParseEvent(t *testing.T) {
putEvent := &clientv3.Event{
Type: clientv3.EventTypePut,
Kv: &mvccpb.KeyValue{
Key: []byte("/demo/key"),
Value: []byte("new"),
ModRevision: 10,
CreateRevision: 10,
},
PrevKv: &mvccpb.KeyValue{Value: []byte("old")},
}
parsed := parseEvent(putEvent)
if parsed == nil || parsed.key != "/demo/key" || parsed.rev != 10 || !parsed.isCreated || parsed.isDeleted {
t.Fatalf("unexpected parsed put event: %+v", parsed)
}
if string(parsed.prevValue) != "old" {
t.Fatalf("unexpected previous value: %q", string(parsed.prevValue))
}
deleteEvent := &clientv3.Event{
Type: clientv3.EventTypeDelete,
Kv: &mvccpb.KeyValue{
Key: []byte("/demo/key"),
ModRevision: 11,
},
}
parsed = parseEvent(deleteEvent)
if parsed == nil || !parsed.isDeleted || parsed.isCreated {
t.Fatalf("unexpected parsed delete event: %+v", parsed)
}
}
func TestWatchChanTransform(t *testing.T) {
wc := &watchChan{}
if wc.transform(nil) != nil {
t.Fatal("expected nil event to stay nil")
}
added := wc.transform(&event{isCreated: true})
if added == nil || added.Type != Added {
t.Fatalf("expected added event, got %+v", added)
}
deleted := wc.transform(&event{isDeleted: true})
if deleted == nil || deleted.Type != Deleted {
t.Fatalf("expected deleted event, got %+v", deleted)
}
modified := wc.transform(&event{})
if modified == nil || modified.Type != Modified {
t.Fatalf("expected modified event, got %+v", modified)
}
}
func TestParseError(t *testing.T) {
compacted := parseError(v3rpc.ErrCompacted)
if compacted == nil || compacted.Type != Error || compacted.Error.Code != http.StatusGone || compacted.Error.Reason != "Expired" {
t.Fatalf("unexpected compacted error event: %+v", compacted)
}
internal := parseError(v3rpc.ErrFutureRev)
if internal == nil || internal.Type != Error || internal.Error.Code != http.StatusInternalServerError || internal.Error.Reason != "InternalError" {
t.Fatalf("unexpected internal error event: %+v", internal)
}
}
func TestWatchChanSendError(t *testing.T) {
wc := &watchChan{
errChan: make(chan error, 1),
ctx: context.TODO(),
}
err := v3rpc.ErrCompacted
wc.sendError(err)
if got := <-wc.errChan; got != err {
t.Fatalf("expected compacted error, got %v", got)
}
}
func TestWatchChanSendEvent(t *testing.T) {
wc := &watchChan{
incomingEventChan: make(chan *event, 1),
ctx: context.TODO(),
}
e := &event{key: "/demo/key"}
wc.sendEvent(e)
if got := <-wc.incomingEventChan; got != e {
t.Fatalf("expected same event pointer, got %+v", got)
}
}