From 2f0ab9027105e2745af88e0b57a611269b7889de Mon Sep 17 00:00:00 2001 From: jdl Date: Wed, 27 Jul 2022 15:22:20 +0200 Subject: [PATCH] testing: WIP --- README.md | 15 +++----- btreeindex.go | 2 + collection.go | 11 +----- collection_test.go | 73 ++++++++++++++++++++++++++++++++--- main_test.go | 4 +- shipping_test.go | 96 +++++++++++++--------------------------------- 6 files changed, 104 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index f269d6d..de8b31a 100644 --- a/README.md +++ b/README.md @@ -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? diff --git a/btreeindex.go b/btreeindex.go index 722bebd..cb24a95 100644 --- a/btreeindex.go +++ b/btreeindex.go @@ -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) } }) diff --git a/collection.go b/collection.go index 554e775..9e2bd1b 100644 --- a/collection.go +++ b/collection.go @@ -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)) } } diff --git a/collection_test.go b/collection_test.go index cbbfbd6..1f23041 100644 --- a/collection_test.go +++ b/collection_test.go @@ -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) + } + }) } diff --git a/main_test.go b/main_test.go index 89e8d55..3285e63 100644 --- a/main_test.go +++ b/main_test.go @@ -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) diff --git a/shipping_test.go b/shipping_test.go index 5d64c1e..917487d 100644 --- a/shipping_test.go +++ b/shipping_test.go @@ -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) - } - } -*/