This repository has been archived on 2022-07-30. You can view files and clone it, but cannot push or open issues/pull-requests.
mdb/mapindex_test.go

324 lines
8.0 KiB
Go

package mdb
/*
func TestMapIndex(t *testing.T) {
type Item struct {
ID uint64
Name string
ExtID string
}
checkIdxOne := func(idx *MapIndex[string, Item], expectedList ...Item) error {
expected := make(map[string]Item, len(expectedList))
for _, i := range expectedList {
expected[i.ExtID] = i
}
if len(expected) != len(idx.m) {
return fmt.Errorf("Expected %d items, but got %d.", len(expected), len(idx.m))
}
for _, e := range expected {
i, ok := idx.Get(e.ExtID)
if !ok {
return fmt.Errorf("Missing item: %v", e)
}
if !reflect.DeepEqual(i, e) {
return fmt.Errorf("Items not equal: %v != %v", i, e)
}
}
return nil
}
checkIdx := func(idx *MapIndex[string, Item], expectedList ...Item) error {
idx.c.db.waitForWAL()
if err := checkIdxOne(idx, expectedList...); err != nil {
return fmt.Errorf("%w: original", err)
}
// Reload the database, collection, and index and re-test.
db := NewPrimary(idx.c.db.root)
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
idx = NewMapIndex(c,
"ExtID",
func(i *Item) string { return i.ExtID },
func(i *Item) bool { return i.ExtID != "" })
db.Start()
return checkIdxOne(idx, expectedList...)
}
run := func(name string, inner func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item])) {
t.Run(name, func(t *testing.T) {
root := filepath.Join(os.TempDir(), randString())
defer os.RemoveAll(root)
db := NewPrimary(root)
defer db.Close()
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
idx := NewMapIndex(c,
"ExtID",
func(i *Item) string { return i.ExtID },
func(i *Item) bool { return i.ExtID != "" })
db.Start()
inner(t, c, idx)
})
}
run("insert item not in index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
item := Item{4, "4", ""}
c.Insert(item)
if err := checkIdx(idx); err != nil {
t.Fatal(err)
}
})
run("insert item in index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
item1 := Item{4, "4", ""}
item2 := Item{5, "5", "abcd"}
c.Insert(item1)
c.Insert(item2)
if err := checkIdx(idx, item2); err != nil {
t.Fatal(err)
}
})
run("insert several items", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
item1 := Item{4, "4", ""}
item2 := Item{5, "5", "abcd"}
item3 := Item{6, "6", ""}
item4 := Item{7, "7", "xyz"}
item5 := Item{8, "8", ""}
item6 := Item{9, "9", "mmm"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
c.Insert(item6)
if err := checkIdx(idx, item2, item4, item6); err != nil {
t.Fatal(err)
}
})
run("insert with conflict", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
item1 := Item{1, "1", "one"}
item2 := Item{2, "2", "one"}
c.Insert(item1)
if _, err := c.Insert(item2); !errors.Is(err, ErrDuplicate) {
t.Fatal(err)
}
})
run("update into index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
item1 := Item{1, "1", ""}
c.Insert(item1)
if err := checkIdx(idx); err != nil {
t.Fatal(err)
}
err := c.Update(1, func(i Item) (Item, error) {
i.ExtID = "xx"
return i, nil
})
if err != nil {
t.Fatal(err)
}
item1.ExtID = "xx"
if err := checkIdx(idx, item1); err != nil {
t.Fatal(err)
}
})
run("update out of index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", ""})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
err := c.Update(1, func(in Item) (Item, error) {
in.Name = "ONE"
return in, nil
})
if err != nil {
t.Fatal(err)
}
if err := checkIdx(idx, Item{2, "2", "two"}); err != nil {
t.Fatal(err)
}
})
run("update out of index conflict", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", ""})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
err := c.Update(1, func(in Item) (Item, error) {
in.ExtID = "two"
return in, nil
})
if !errors.Is(err, ErrDuplicate) {
t.Fatal(err)
}
})
run("update within index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
err := c.Update(2, func(in Item) (Item, error) {
in.ExtID = "TWO"
return in, nil
})
if err != nil {
t.Fatal(err)
}
if err := checkIdx(idx, Item{1, "1", "one"}, Item{2, "2", "TWO"}); err != nil {
t.Fatal(err)
}
})
run("update using index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
err := idx.Update("one", func(in Item) (Item, error) {
in.Name = "_1_"
return in, nil
})
if err != nil {
t.Fatal(err)
}
if err := checkIdx(idx, Item{1, "_1_", "one"}, Item{2, "2", "two"}); err != nil {
t.Fatal(err)
}
})
run("update using index not found", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{3, "3", ""})
err := idx.Update("onex", func(in Item) (Item, error) {
in.Name = "_1_"
return in, nil
})
if !errors.Is(err, ErrNotFound) {
t.Fatal(err)
}
})
run("update using index caller error", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{3, "3", ""})
myErr := errors.New("Mine")
err := idx.Update("one", func(in Item) (Item, error) {
in.Name = "_1_"
return in, myErr
})
if !errors.Is(err, myErr) {
t.Fatal(err)
}
})
run("update using index mismatched IDs", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
err := idx.Update("one", func(in Item) (Item, error) {
in.ExtID = "onex"
return in, nil
})
if !errors.Is(err, ErrMismatchedIDs) {
t.Fatal(err)
}
})
run("delete out of index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
c.Delete(3)
if err := checkIdx(idx, Item{1, "1", "one"}, Item{2, "2", "two"}); err != nil {
t.Fatal(err)
}
})
run("delete from index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
c.Delete(2)
if err := checkIdx(idx, Item{1, "1", "one"}); err != nil {
t.Fatal(err)
}
})
run("delete using index", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
idx.Delete("two")
if err := checkIdx(idx, Item{1, "1", "one"}); err != nil {
t.Fatal(err)
}
})
run("delete using index not found", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
c.Insert(Item{1, "1", "one"})
c.Insert(Item{2, "2", "two"}) // In index.
c.Insert(Item{3, "3", ""})
idx.Delete("onex")
if err := checkIdx(idx, Item{1, "1", "one"}, Item{2, "2", "two"}); err != nil {
t.Fatal(err)
}
})
run("check name", func(t *testing.T, c *Collection[Item], idx *MapIndex[string, Item]) {
if idx.name() != "ExtID" {
t.Fatal(idx.name())
}
})
}
func TestMapIndexLoadError(t *testing.T) {
type Item struct {
ID uint64
Name string
ExtID string
}
root := filepath.Join(os.TempDir(), randString())
defer os.RemoveAll(root)
db := NewPrimary(root)
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
db.Start()
defer db.Close()
c.Insert(Item{1, "one", "x"})
c.Insert(Item{2, "two", "x"})
c.Insert(Item{3, "three", "y"})
c.Insert(Item{4, "x", ""})
idx := NewMapIndex(c,
"ExtID",
func(i *Item) string { return i.ExtID },
func(i *Item) bool { return i.ExtID != "" })
err := idx.load(c.items.m)
if !errors.Is(err, ErrDuplicate) {
t.Fatal(err)
}
}
*/