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 }