2022-07-26 12:02:32 +00:00
|
|
|
package mdb
|
|
|
|
|
2022-07-26 15:13:16 +00:00
|
|
|
/*
|
2022-07-26 12:02:32 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2022-07-26 15:13:16 +00:00
|
|
|
*/
|