testing: WIP

master
jdl 2022-07-27 15:22:20 +02:00
parent d7f9e92fc4
commit 2f0ab90271
6 changed files with 104 additions and 97 deletions

View File

@ -4,15 +4,10 @@ An in-process, in-memory database for Go.
## TO DO
* mapindex_test.go
* ~~TestFullMapIndex~~
* btreeindex_test.go
* TestPartialBTreeIndex
* btreeiterator_test.go (?)
* collection
* database
* WAL shipping
* WAL shipping with network disconnects
* database: test for concurrent writers
* Create writers in different ID ranges of Users and Accounts
* Check results at end.
* database: WAL shipping
* database: WAL shipping with network disconnects
* BTreeIndex:
* Should insert panic if item is replaced?

View File

@ -118,6 +118,7 @@ func (t *BTreeIndex[T]) Len() int {
func (t *BTreeIndex[T]) insert(item *T) {
if t.include == nil || t.include(item) {
t.modify(func(bt *btree.BTreeG[*T]) {
// TODO: Panic if replaces.
bt.ReplaceOrInsert(item)
})
}
@ -129,6 +130,7 @@ func (t *BTreeIndex[T]) update(old, new *T) {
bt.Delete(old)
}
if t.include == nil || t.include(new) {
// TODO: Panic if replaces.
bt.ReplaceOrInsert(new)
}
})

View File

@ -175,15 +175,8 @@ func (c Collection[T]) Get(id uint64) (t T, ok bool) {
// ----------------------------------------------------------------------------
func (c *Collection[T]) loadData() {
toRemove := []int{}
for i, idx := range c.indices {
if err := idx.load(c.items.m); err != nil {
toRemove = append([]int{i}, toRemove...)
}
}
for _, i := range toRemove {
c.indices = append(c.indices[:i], c.indices[i+1:]...)
for _, idx := range c.indices {
must(idx.load(c.items.m))
}
}

View File

@ -2,6 +2,7 @@ package mdb
import (
"errors"
"reflect"
"testing"
)
@ -74,10 +75,72 @@ func TestCollection(t *testing.T) {
}
})
// Update w/ failed valiation
testWithDB(t, "update failed validation", func(t *testing.T, db *DB) {
c := db.Users.c
user := User{ID: c.NextID(), Name: "adsf", Email: "a@b.com"}
if _, err := c.Insert(user); err != nil {
t.Fatal(err)
}
// Delete on secondary
// Delete not found
// Get found
// Get not found
err := c.Update(user.ID, func(u User) (User, error) {
u.Name = ""
return u, nil
})
if !errors.Is(err, ErrInvalidName) {
t.Fatal(err)
}
})
testWithDB(t, "delete on secondary", func(t *testing.T, db *DB) {
defer func() {
if err := recover(); err == nil {
t.Fatal("No panic")
}
}()
c := db.Users.c
user := User{ID: c.NextID(), Name: "adsf", Email: "a@b.com"}
if _, err := c.Insert(user); err != nil {
t.Fatal(err)
}
c.primary = false
c.Delete(1)
})
testWithDB(t, "delete not found", func(t *testing.T, db *DB) {
c := db.Users.c
user := User{ID: c.NextID(), Name: "adsf", Email: "a@b.com"}
if _, err := c.Insert(user); err != nil {
t.Fatal(err)
}
c.Delete(user.ID + 1) // Does nothing.
})
testWithDB(t, "get", func(t *testing.T, db *DB) {
c := db.Users.c
user := User{ID: c.NextID(), Name: "adsf", Email: "a@b.com"}
if _, err := c.Insert(user); err != nil {
t.Fatal(err)
}
u2, ok := c.Get(user.ID)
if !ok || !reflect.DeepEqual(user, u2) {
t.Fatal(ok, u2, user)
}
})
testWithDB(t, "get not found", func(t *testing.T, db *DB) {
c := db.Users.c
user := User{ID: c.NextID(), Name: "adsf", Email: "a@b.com"}
if _, err := c.Insert(user); err != nil {
t.Fatal(err)
}
u2, ok := c.Get(user.ID - 1)
if ok {
t.Fatal(ok, u2)
}
})
}

View File

@ -15,9 +15,7 @@ func TestMain(m *testing.M) {
func testWithDB(t *testing.T, name string, inner func(t *testing.T, db *DB)) {
t.Run(name, func(t *testing.T) {
root, err := os.MkdirTemp("", "")
if err != nil {
t.Fatal(err)
}
must(err)
defer os.RemoveAll(root)
db := OpenDB(root, true)

View File

@ -1,76 +1,32 @@
package mdb
/*
func TestLogShip(t *testing.T) {
type Item struct {
ID uint64
Name string
import (
"os"
"testing"
"git.crumpington.com/private/mdb/testconn"
)
func TestShipping(t *testing.T) {
run := func(name string, inner func(t *testing.T, db1 *DB, db2 *DB, network *testconn.Network)) {
t.Run(name, func(t *testing.T) {
root1, err := os.MkdirTemp("", "")
must(err)
defer os.RemoveAll(root1)
root2, err := os.MkdirTemp("", "")
must(err)
defer os.RemoveAll(root2)
db1 := OpenDB(root1, true)
defer db1.Close()
db2 := OpenDB(root2, false)
defer db2.Close()
inner(t, db1, db2, testconn.NewNetwork())
})
}
newDB := func(root string, primary bool) (*Database, *Collection[Item]) {
var db *Database
if primary {
db = NewPrimary(root)
} else {
db = NewSecondary(root)
}
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
NewBTreeIndex(c,
func(i, j *Item) bool { return i.Name < j.Name },
func(i *Item) bool { return i.Name != "" })
return db, c
}
root1 := filepath.Join(os.TempDir(), randString())
root2 := filepath.Join(os.TempDir(), randString())
//log.Print(root1, " --> ", root2)
defer os.RemoveAll(root1)
defer os.RemoveAll(root2)
dbLeader, colLeader := newDB(root1, true)
dbLeader.Start()
defer dbLeader.Close()
dbFollower, _ := newDB(root2, false)
dbFollower.Start()
defer dbFollower.Close()
c1, c2 := net.Pipe()
go dbLeader.SyncSend(c1)
go dbFollower.SyncRecv(c2)
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
item1, _ = colLeader.Insert(item1)
item2, _ = colLeader.Insert(item2)
item3, _ = colLeader.Insert(item3)
item4, _ = colLeader.Insert(item4)
item5, _ = colLeader.Insert(item5)
colLeader.Delete(item2.ID)
colLeader.Update(item4.ID, func(old Item) (Item, error) {
old.Name = "UPDATED"
return old, nil
run("simple", func(t *testing.T, db1, db2 *DB, network *testconn.Network) {
// TODO
})
dbLeader.waitForWAL()
dbFollower.waitForWAL()
dbLeader, colLeader = newDB(root1, true)
dbLeader.Start()
dbFollower, colFollower := newDB(root2, false)
dbFollower.Start()
m1 := colLeader.items.m
m2 := colFollower.items.m
if len(m1) != len(m2) {
t.Fatal(m1, m2)
}
}
*/