github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/swarm/shed/index_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package shed 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "sort" 24 "testing" 25 "time" 26 27 "github.com/syndtr/goleveldb/leveldb" 28 ) 29 30 // Index functions for the index that is used in tests in this file. 31 var retrievalIndexFuncs = IndexFuncs{ 32 EncodeKey: func(fields IndexItem) (key []byte, err error) { 33 return fields.Address, nil 34 }, 35 DecodeKey: func(key []byte) (e IndexItem, err error) { 36 e.Address = key 37 return e, nil 38 }, 39 EncodeValue: func(fields IndexItem) (value []byte, err error) { 40 b := make([]byte, 8) 41 binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) 42 value = append(b, fields.Data...) 43 return value, nil 44 }, 45 DecodeValue: func(value []byte) (e IndexItem, err error) { 46 e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) 47 e.Data = value[8:] 48 return e, nil 49 }, 50 } 51 52 // TestIndex validates put, get and delete functions of the Index implementation. 53 func TestIndex(t *testing.T) { 54 db, cleanupFunc := newTestDB(t) 55 defer cleanupFunc() 56 57 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 t.Run("put", func(t *testing.T) { 63 want := IndexItem{ 64 Address: []byte("put-hash"), 65 Data: []byte("DATA"), 66 StoreTimestamp: time.Now().UTC().UnixNano(), 67 } 68 69 err := index.Put(want) 70 if err != nil { 71 t.Fatal(err) 72 } 73 got, err := index.Get(IndexItem{ 74 Address: want.Address, 75 }) 76 if err != nil { 77 t.Fatal(err) 78 } 79 checkIndexItem(t, got, want) 80 81 t.Run("overwrite", func(t *testing.T) { 82 want := IndexItem{ 83 Address: []byte("put-hash"), 84 Data: []byte("New DATA"), 85 StoreTimestamp: time.Now().UTC().UnixNano(), 86 } 87 88 err = index.Put(want) 89 if err != nil { 90 t.Fatal(err) 91 } 92 got, err := index.Get(IndexItem{ 93 Address: want.Address, 94 }) 95 if err != nil { 96 t.Fatal(err) 97 } 98 checkIndexItem(t, got, want) 99 }) 100 }) 101 102 t.Run("put in batch", func(t *testing.T) { 103 want := IndexItem{ 104 Address: []byte("put-in-batch-hash"), 105 Data: []byte("DATA"), 106 StoreTimestamp: time.Now().UTC().UnixNano(), 107 } 108 109 batch := new(leveldb.Batch) 110 index.PutInBatch(batch, want) 111 err := db.WriteBatch(batch) 112 if err != nil { 113 t.Fatal(err) 114 } 115 got, err := index.Get(IndexItem{ 116 Address: want.Address, 117 }) 118 if err != nil { 119 t.Fatal(err) 120 } 121 checkIndexItem(t, got, want) 122 123 t.Run("overwrite", func(t *testing.T) { 124 want := IndexItem{ 125 Address: []byte("put-in-batch-hash"), 126 Data: []byte("New DATA"), 127 StoreTimestamp: time.Now().UTC().UnixNano(), 128 } 129 130 batch := new(leveldb.Batch) 131 index.PutInBatch(batch, want) 132 db.WriteBatch(batch) 133 if err != nil { 134 t.Fatal(err) 135 } 136 got, err := index.Get(IndexItem{ 137 Address: want.Address, 138 }) 139 if err != nil { 140 t.Fatal(err) 141 } 142 checkIndexItem(t, got, want) 143 }) 144 }) 145 146 t.Run("put in batch twice", func(t *testing.T) { 147 // ensure that the last item of items with the same db keys 148 // is actually saved 149 batch := new(leveldb.Batch) 150 address := []byte("put-in-batch-twice-hash") 151 152 // put the first item 153 index.PutInBatch(batch, IndexItem{ 154 Address: address, 155 Data: []byte("DATA"), 156 StoreTimestamp: time.Now().UTC().UnixNano(), 157 }) 158 159 want := IndexItem{ 160 Address: address, 161 Data: []byte("New DATA"), 162 StoreTimestamp: time.Now().UTC().UnixNano(), 163 } 164 // then put the item that will produce the same key 165 // but different value in the database 166 index.PutInBatch(batch, want) 167 db.WriteBatch(batch) 168 if err != nil { 169 t.Fatal(err) 170 } 171 got, err := index.Get(IndexItem{ 172 Address: address, 173 }) 174 if err != nil { 175 t.Fatal(err) 176 } 177 checkIndexItem(t, got, want) 178 }) 179 180 t.Run("delete", func(t *testing.T) { 181 want := IndexItem{ 182 Address: []byte("delete-hash"), 183 Data: []byte("DATA"), 184 StoreTimestamp: time.Now().UTC().UnixNano(), 185 } 186 187 err := index.Put(want) 188 if err != nil { 189 t.Fatal(err) 190 } 191 got, err := index.Get(IndexItem{ 192 Address: want.Address, 193 }) 194 if err != nil { 195 t.Fatal(err) 196 } 197 checkIndexItem(t, got, want) 198 199 err = index.Delete(IndexItem{ 200 Address: want.Address, 201 }) 202 if err != nil { 203 t.Fatal(err) 204 } 205 206 wantErr := leveldb.ErrNotFound 207 got, err = index.Get(IndexItem{ 208 Address: want.Address, 209 }) 210 if err != wantErr { 211 t.Fatalf("got error %v, want %v", err, wantErr) 212 } 213 }) 214 215 t.Run("delete in batch", func(t *testing.T) { 216 want := IndexItem{ 217 Address: []byte("delete-in-batch-hash"), 218 Data: []byte("DATA"), 219 StoreTimestamp: time.Now().UTC().UnixNano(), 220 } 221 222 err := index.Put(want) 223 if err != nil { 224 t.Fatal(err) 225 } 226 got, err := index.Get(IndexItem{ 227 Address: want.Address, 228 }) 229 if err != nil { 230 t.Fatal(err) 231 } 232 checkIndexItem(t, got, want) 233 234 batch := new(leveldb.Batch) 235 index.DeleteInBatch(batch, IndexItem{ 236 Address: want.Address, 237 }) 238 err = db.WriteBatch(batch) 239 if err != nil { 240 t.Fatal(err) 241 } 242 243 wantErr := leveldb.ErrNotFound 244 got, err = index.Get(IndexItem{ 245 Address: want.Address, 246 }) 247 if err != wantErr { 248 t.Fatalf("got error %v, want %v", err, wantErr) 249 } 250 }) 251 } 252 253 // TestIndex_iterate validates index iterator functions for correctness. 254 func TestIndex_iterate(t *testing.T) { 255 db, cleanupFunc := newTestDB(t) 256 defer cleanupFunc() 257 258 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 259 if err != nil { 260 t.Fatal(err) 261 } 262 263 items := []IndexItem{ 264 { 265 Address: []byte("iterate-hash-01"), 266 Data: []byte("data80"), 267 }, 268 { 269 Address: []byte("iterate-hash-03"), 270 Data: []byte("data22"), 271 }, 272 { 273 Address: []byte("iterate-hash-05"), 274 Data: []byte("data41"), 275 }, 276 { 277 Address: []byte("iterate-hash-02"), 278 Data: []byte("data84"), 279 }, 280 { 281 Address: []byte("iterate-hash-06"), 282 Data: []byte("data1"), 283 }, 284 } 285 batch := new(leveldb.Batch) 286 for _, i := range items { 287 index.PutInBatch(batch, i) 288 } 289 err = db.WriteBatch(batch) 290 if err != nil { 291 t.Fatal(err) 292 } 293 item04 := IndexItem{ 294 Address: []byte("iterate-hash-04"), 295 Data: []byte("data0"), 296 } 297 err = index.Put(item04) 298 if err != nil { 299 t.Fatal(err) 300 } 301 items = append(items, item04) 302 303 sort.SliceStable(items, func(i, j int) bool { 304 return bytes.Compare(items[i].Address, items[j].Address) < 0 305 }) 306 307 t.Run("all", func(t *testing.T) { 308 var i int 309 err := index.IterateAll(func(item IndexItem) (stop bool, err error) { 310 if i > len(items)-1 { 311 return true, fmt.Errorf("got unexpected index item: %#v", item) 312 } 313 want := items[i] 314 checkIndexItem(t, item, want) 315 i++ 316 return false, nil 317 }) 318 if err != nil { 319 t.Fatal(err) 320 } 321 }) 322 323 t.Run("from", func(t *testing.T) { 324 startIndex := 2 325 i := startIndex 326 err := index.IterateFrom(items[startIndex], func(item IndexItem) (stop bool, err error) { 327 if i > len(items)-1 { 328 return true, fmt.Errorf("got unexpected index item: %#v", item) 329 } 330 want := items[i] 331 checkIndexItem(t, item, want) 332 i++ 333 return false, nil 334 }) 335 if err != nil { 336 t.Fatal(err) 337 } 338 }) 339 340 t.Run("stop", func(t *testing.T) { 341 var i int 342 stopIndex := 3 343 var count int 344 err := index.IterateAll(func(item IndexItem) (stop bool, err error) { 345 if i > len(items)-1 { 346 return true, fmt.Errorf("got unexpected index item: %#v", item) 347 } 348 want := items[i] 349 checkIndexItem(t, item, want) 350 count++ 351 if i == stopIndex { 352 return true, nil 353 } 354 i++ 355 return false, nil 356 }) 357 if err != nil { 358 t.Fatal(err) 359 } 360 wantItemsCount := stopIndex + 1 361 if count != wantItemsCount { 362 t.Errorf("got %v items, expected %v", count, wantItemsCount) 363 } 364 }) 365 366 t.Run("no overflow", func(t *testing.T) { 367 secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) 368 if err != nil { 369 t.Fatal(err) 370 } 371 372 secondIndexItem := IndexItem{ 373 Address: []byte("iterate-hash-10"), 374 Data: []byte("data-second"), 375 } 376 err = secondIndex.Put(secondIndexItem) 377 if err != nil { 378 t.Fatal(err) 379 } 380 381 var i int 382 err = index.IterateAll(func(item IndexItem) (stop bool, err error) { 383 if i > len(items)-1 { 384 return true, fmt.Errorf("got unexpected index item: %#v", item) 385 } 386 want := items[i] 387 checkIndexItem(t, item, want) 388 i++ 389 return false, nil 390 }) 391 if err != nil { 392 t.Fatal(err) 393 } 394 395 i = 0 396 err = secondIndex.IterateAll(func(item IndexItem) (stop bool, err error) { 397 if i > 1 { 398 return true, fmt.Errorf("got unexpected index item: %#v", item) 399 } 400 checkIndexItem(t, item, secondIndexItem) 401 i++ 402 return false, nil 403 }) 404 if err != nil { 405 t.Fatal(err) 406 } 407 }) 408 } 409 410 // checkIndexItem is a test helper function that compares if two Index items are the same. 411 func checkIndexItem(t *testing.T, got, want IndexItem) { 412 t.Helper() 413 414 if !bytes.Equal(got.Address, want.Address) { 415 t.Errorf("got hash %q, expected %q", string(got.Address), string(want.Address)) 416 } 417 if !bytes.Equal(got.Data, want.Data) { 418 t.Errorf("got data %q, expected %q", string(got.Data), string(want.Data)) 419 } 420 if got.StoreTimestamp != want.StoreTimestamp { 421 t.Errorf("got store timestamp %v, expected %v", got.StoreTimestamp, want.StoreTimestamp) 422 } 423 if got.AccessTimestamp != want.AccessTimestamp { 424 t.Errorf("got access timestamp %v, expected %v", got.AccessTimestamp, want.AccessTimestamp) 425 } 426 }