github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/sync/etcdv3/etcd_test.go (about)

     1  package etcdv3
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	gohan_sync "github.com/cloudwan/gohan/sync"
     8  	etcd "github.com/coreos/etcd/clientv3"
     9  	"golang.org/x/net/context"
    10  )
    11  
    12  var endpoints = []string{"localhost:2379"}
    13  
    14  func TestNewSyncTimeout(t *testing.T) {
    15  	done := make(chan struct{})
    16  	go func() {
    17  		_, err := NewSync([]string{"invalid:1000"}, time.Millisecond*100)
    18  		if err == nil {
    19  			t.Errorf("nil returned for error")
    20  		}
    21  		close(done)
    22  	}()
    23  	select {
    24  	case <-time.NewTimer(time.Millisecond * 200).C:
    25  		t.Errorf("timeout didn't work")
    26  	case <-done:
    27  	}
    28  }
    29  
    30  func TestNonEmptyUpdate(t *testing.T) {
    31  	sync := newSync(t)
    32  	sync.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
    33  
    34  	path := "/path/to/somewhere"
    35  	data := "blabla"
    36  	err := sync.Update(path, data)
    37  	if err != nil {
    38  		t.Errorf("unexpected error")
    39  	}
    40  
    41  	node, err := sync.Fetch(path)
    42  	if err != nil {
    43  		t.Errorf("unexpected error")
    44  	}
    45  	if node.Key != path || node.Value != data || len(node.Children) != 0 {
    46  		t.Errorf("unexpected node: %+v", node)
    47  	}
    48  
    49  	err = sync.Delete(path)
    50  	if err != nil {
    51  		t.Errorf("unexpected error")
    52  	}
    53  
    54  	node, err = sync.Fetch(path)
    55  	if err == nil {
    56  		t.Errorf("unexpected non error")
    57  	}
    58  }
    59  
    60  func TestEmptyUpdate(t *testing.T) {
    61  	sync := newSync(t)
    62  	sync.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
    63  
    64  	path := "/path/to/somewhere"
    65  	data := ""
    66  	err := sync.Update(path, data)
    67  	if err != nil {
    68  		t.Errorf("unexpected error")
    69  	}
    70  
    71  	// not found because v3 doesn't support directories
    72  	_, err = sync.Fetch(path)
    73  	if err == nil {
    74  		t.Errorf("unexpected error")
    75  	}
    76  }
    77  
    78  func TestRecursiveUpdate(t *testing.T) {
    79  	sync := newSync(t)
    80  	sync.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
    81  
    82  	base := "/path/to/somewhere"
    83  	items := map[string]string{
    84  		base:                 "",
    85  		base + "/inside":     "inside",
    86  		base + "/else":       "",
    87  		base + "/else/child": "child",
    88  	}
    89  
    90  	for path, data := range items {
    91  		err := sync.Update(path, data)
    92  		if err != nil {
    93  			t.Errorf("unexpected error")
    94  		}
    95  	}
    96  	err := sync.Update(base+"invalid", "should not be included")
    97  	if err != nil {
    98  		t.Errorf("unexpected error")
    99  	}
   100  
   101  	// not found because v3 doesn't support directories
   102  	node, err := sync.Fetch(base)
   103  	if err != nil {
   104  		t.Errorf("unexpected error")
   105  	}
   106  
   107  	if node.Key != base || node.Value != items[base] || len(node.Children) != 2 {
   108  		t.Errorf("unexpected node: %+v", node)
   109  	}
   110  	if node.Children[0].Key != base+"/else" || node.Children[0].Value != items[base+"/else"] || len(node.Children[0].Children) != 1 {
   111  		t.Errorf("unexpected node: %+v", node.Children[0])
   112  	}
   113  	if node.Children[0].Children[0].Key != base+"/else/child" || node.Children[0].Children[0].Value != items[base+"/else/child"] || len(node.Children[0].Children[0].Children) != 0 {
   114  		t.Errorf("unexpected node: %+v", node.Children[0].Children[0])
   115  	}
   116  	if node.Children[1].Key != base+"/inside" || node.Children[1].Value != items[base+"/inside"] || len(node.Children[1].Children) != 0 {
   117  		t.Errorf("unexpected node: %+v", node.Children[1])
   118  	}
   119  }
   120  
   121  func TestLockUnblocking(t *testing.T) {
   122  	sync0 := newSync(t)
   123  	sync1 := newSync(t)
   124  	sync0.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
   125  
   126  	path := "/path/lock"
   127  	err := sync0.Lock(path, false)
   128  	if err != nil {
   129  		t.Errorf("unexpected error")
   130  	}
   131  	err = sync1.Lock(path, false)
   132  	if err == nil {
   133  		t.Errorf("unexpected non error")
   134  	}
   135  
   136  	if sync0.HasLock(path) != true {
   137  		t.Errorf("unexpected false")
   138  	}
   139  	if sync1.HasLock(path) != false {
   140  		t.Errorf("unexpected true")
   141  	}
   142  
   143  	err = sync0.Unlock(path)
   144  	if err != nil {
   145  		t.Errorf("unexpected error")
   146  	}
   147  	err = sync1.Lock(path, false)
   148  	if err != nil {
   149  		t.Errorf("unexpected  error")
   150  	}
   151  
   152  	if sync0.HasLock(path) != false {
   153  		t.Errorf("unexpected true")
   154  	}
   155  	if sync1.HasLock(path) != true {
   156  		t.Errorf("unexpected false")
   157  	}
   158  }
   159  
   160  func TestLockBlocking(t *testing.T) {
   161  	sync0 := newSync(t)
   162  	sync1 := newSync(t)
   163  	sync0.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
   164  
   165  	path := "/path/lock"
   166  	err := sync0.Lock(path, true)
   167  	if err != nil {
   168  		t.Errorf("unexpected error")
   169  	}
   170  	locked1 := make(chan struct{})
   171  	go func() {
   172  		err := sync1.Lock(path, true)
   173  		if err != nil {
   174  			t.Errorf("unexpected error")
   175  		}
   176  		close(locked1)
   177  	}()
   178  
   179  	time.Sleep(time.Millisecond * 100)
   180  	select {
   181  	case <-locked1:
   182  		t.Errorf("blocking failed")
   183  	default:
   184  	}
   185  
   186  	if sync0.HasLock(path) != true {
   187  		t.Errorf("unexpected false")
   188  	}
   189  	if sync1.HasLock(path) != false {
   190  		t.Errorf("unexpected true")
   191  	}
   192  
   193  	err = sync0.Unlock(path)
   194  	if err != nil {
   195  		t.Errorf("unexpected error")
   196  	}
   197  	time.Sleep(time.Millisecond * 200)
   198  	<-locked1
   199  
   200  	if sync0.HasLock(path) != false {
   201  		t.Errorf("unexpected true")
   202  	}
   203  	if sync1.HasLock(path) != true {
   204  		t.Errorf("unexpected false")
   205  	}
   206  }
   207  
   208  func TestWatch(t *testing.T) {
   209  	sync := newSync(t)
   210  	sync.etcdClient.Delete(context.Background(), "/", etcd.WithPrefix())
   211  
   212  	path := "/path/to/watch"
   213  	responseChan := make(chan *gohan_sync.Event)
   214  	stopChan := make(chan bool)
   215  
   216  	sync.etcdClient.Put(context.Background(), path+"/existing", `{"existing": true}`)
   217  
   218  	go func() {
   219  		err := sync.Watch(path, responseChan, stopChan, gohan_sync.RevisionCurrent)
   220  		if err != nil {
   221  			t.Errorf("failed to watch")
   222  		}
   223  	}()
   224  
   225  	resp := <-responseChan
   226  	if resp.Action != "get" || resp.Key != path+"/existing" || resp.Data["existing"].(bool) != true {
   227  		t.Errorf("mismatch response: %+v", resp)
   228  	}
   229  
   230  	sync.etcdClient.Put(context.Background(), path+"/new", `{"existing": false}`)
   231  	resp = <-responseChan
   232  	if resp.Action != "set" || resp.Key != path+"/new" || resp.Data["existing"].(bool) != false {
   233  		t.Errorf("mismatch response: %+v", resp)
   234  	}
   235  
   236  	sync.etcdClient.Delete(context.Background(), path+"/existing")
   237  	resp = <-responseChan
   238  	if resp.Action != "delete" || resp.Key != path+"/existing" || len(resp.Data) != 0 {
   239  		t.Errorf("mismatch response: %+v", resp)
   240  	}
   241  }
   242  
   243  func newSync(t *testing.T) *Sync {
   244  	sync, err := NewSync(endpoints, time.Millisecond*100)
   245  	if err != nil {
   246  		t.Errorf("unexpected error")
   247  	}
   248  	return sync
   249  }