jldb/mdb/db-testcases_test.go
2023-10-16 08:50:19 +00:00

855 lines
22 KiB
Go

package mdb
import (
"errors"
"fmt"
"reflect"
"strings"
"testing"
"git.crumpington.com/public/jldb/lib/errs"
)
type DBTestCase struct {
Name string
Steps []DBTestStep
}
type DBTestStep struct {
Name string
Update func(t *testing.T, db TestDB, tx *Snapshot) error
ExpectedUpdateError error
State DBState
}
type DBState struct {
UsersByID []User
UsersByEmail []User
UsersByName []User
UsersByBlocked []User
DataByID []UserDataItem
DataByName []UserDataItem
}
var testDBTestCases = []DBTestCase{{
Name: "Insert update",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user, ok := db.Users.ByID.Get(tx, &User{ID: 1})
if !ok {
return errs.NotFound
}
user.Name = "Bob"
user.Email = "b@c.com"
return db.Users.Update(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Bob", Email: "b@c.com"}},
UsersByEmail: []User{{ID: 1, Name: "Bob", Email: "b@c.com"}},
UsersByName: []User{{ID: 1, Name: "Bob", Email: "b@c.com"}},
},
}},
}, {
Name: "Insert delete",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Delete",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
return db.Users.Delete(tx, 1)
},
State: DBState{},
}},
}, {
Name: "Insert duplicate one tx (ID)",
Steps: []DBTestStep{{
Name: "Insert with duplicate",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
if err := db.Users.Insert(tx, user); err != nil {
return err
}
user2 := &User{ID: 1, Name: "Bob", Email: "b@c.com"}
return db.Users.Insert(tx, user2)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{},
}},
}, {
Name: "Insert duplicate one tx (email)",
Steps: []DBTestStep{{
Name: "Insert with duplicate",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
if err := db.Users.Insert(tx, user); err != nil {
return err
}
user2 := &User{ID: 2, Name: "Bob", Email: "a@b.com"}
return db.Users.Insert(tx, user2)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{},
}},
}, {
Name: "Insert duplicate two txs (ID)",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Insert duplicate",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Bob", Email: "b@c.com"}
return db.Users.Insert(tx, user)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Insert duplicate two txs (email)",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Insert duplicate",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 2, Name: "Bob", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Insert read-only snapshot",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(db.Snapshot(), user)
},
ExpectedUpdateError: errs.ReadOnly,
}},
}, {
Name: "Insert partial index",
Steps: []DBTestStep{{
Name: "Insert Alice",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 5, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Insert Bob",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 2, Name: "Bob", Email: "b@c.com", Blocked: true}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{
{ID: 2, Name: "Bob", Email: "b@c.com", Blocked: true},
{ID: 5, Name: "Alice", Email: "a@b.com"},
},
UsersByEmail: []User{
{ID: 5, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com", Blocked: true},
},
UsersByName: []User{
{ID: 5, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com", Blocked: true},
},
UsersByBlocked: []User{
{ID: 2, Name: "Bob", Email: "b@c.com", Blocked: true},
},
},
}},
}, {
Name: "Update not found",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 5, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 4, Name: "Alice", Email: "x@y.com"}
return db.Users.Update(tx, user)
},
ExpectedUpdateError: errs.NotFound,
State: DBState{
UsersByID: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 5, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Update read-only snapshot",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user, ok := db.Users.ByID.Get(tx, &User{ID: 1})
if !ok {
return errs.NotFound
}
user.Name = "Bob"
user.Email = "b@c.com"
return db.Users.Update(db.Snapshot(), user)
},
ExpectedUpdateError: errs.ReadOnly,
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Insert into two collections",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
if err := db.Users.Insert(tx, user); err != nil {
return err
}
data := &UserDataItem{ID: 1, UserID: user.ID, Name: "Item1", Data: "xyz"}
return db.UserData.Insert(tx, data)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
DataByID: []UserDataItem{{ID: 1, UserID: 1, Name: "Item1", Data: "xyz"}},
DataByName: []UserDataItem{{ID: 1, UserID: 1, Name: "Item1", Data: "xyz"}},
},
}},
}, {
Name: "Update into index",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}
return db.Users.Update(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByBlocked: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
},
}},
}, {
Name: "Update out of index",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
UsersByBlocked: []User{{ID: 1, Name: "Alice", Email: "a@b.com", Blocked: true}},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Update(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Update duplicate one tx",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user1 := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
if err := db.Users.Insert(tx, user1); err != nil {
return err
}
user2 := &User{ID: 2, Name: "Bob", Email: "b@c.com"}
if err := db.Users.Insert(tx, user2); err != nil {
return err
}
user2.Email = "a@b.com"
return db.Users.Update(tx, user2)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{},
}},
}, {
Name: "Update duplicate two txs",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user1 := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
if err := db.Users.Insert(tx, user1); err != nil {
return err
}
user2 := &User{ID: 2, Name: "Bob", Email: "b@c.com"}
return db.Users.Insert(tx, user2)
},
State: DBState{
UsersByID: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
UsersByEmail: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
UsersByName: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
},
}, {
Name: "Update",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
u, ok := db.Users.ByID.Get(tx, &User{ID: 2})
if !ok {
return errs.NotFound
}
u.Email = "a@b.com"
return db.Users.Update(tx, u)
},
ExpectedUpdateError: errs.Duplicate,
State: DBState{
UsersByID: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
UsersByEmail: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
UsersByName: []User{
{ID: 1, Name: "Alice", Email: "a@b.com"},
{ID: 2, Name: "Bob", Email: "b@c.com"},
},
},
}},
}, {
Name: "Delete read only",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Delete",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
return db.Users.Delete(db.Snapshot(), 1)
},
ExpectedUpdateError: errs.ReadOnly,
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Delete not found",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Delete",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
return db.Users.Delete(tx, 2)
},
ExpectedUpdateError: errs.NotFound,
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Index general",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
user := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
return db.Users.Insert(tx, user)
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Get found",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
expected := &User{ID: 1, Name: "Alice", Email: "a@b.com"}
u, ok := db.Users.ByID.Get(tx, &User{ID: 1})
if !ok {
return errs.NotFound
}
if !reflect.DeepEqual(u, expected) {
return errors.New("Not equal (id)")
}
u, ok = db.Users.ByEmail.Get(tx, &User{Email: "a@b.com"})
if !ok {
return errs.NotFound
}
if !reflect.DeepEqual(u, expected) {
return errors.New("Not equal (email)")
}
return nil
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Get not found",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
if _, ok := db.Users.ByID.Get(tx, &User{ID: 2}); ok {
return errors.New("Found (id)")
}
if _, ok := db.Users.ByEmail.Get(tx, &User{Email: "x@b.com"}); ok {
return errors.New("Found (email)")
}
return nil
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Has (true)",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
if ok := db.Users.ByID.Has(tx, &User{ID: 1}); !ok {
return errors.New("Not found (id)")
}
if ok := db.Users.ByEmail.Has(tx, &User{Email: "a@b.com"}); !ok {
return errors.New("Not found (email)")
}
return nil
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}, {
Name: "Has (false)",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
if ok := db.Users.ByID.Has(tx, &User{ID: 2}); ok {
return errors.New("Found (id)")
}
if ok := db.Users.ByEmail.Has(tx, &User{Email: "x@b.com"}); ok {
return errors.New("Found (email)")
}
return nil
},
State: DBState{
UsersByID: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByEmail: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
UsersByName: []User{{ID: 1, Name: "Alice", Email: "a@b.com"}},
},
}},
}, {
Name: "Mutate while iterating",
Steps: []DBTestStep{{
Name: "Insert",
Update: func(t *testing.T, db TestDB, tx *Snapshot) error {
for i := 0; i < 4; i++ {
user := &User{
ID: uint64(i) + 1,
Name: fmt.Sprintf("User%d", i),
Email: fmt.Sprintf("user.%d@x.com", i),
}
if err := db.Users.Insert(tx, user); err != nil {
return err
}
}
return nil
},
State: DBState{
UsersByID: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
},
UsersByEmail: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
},
UsersByName: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
},
},
}, {
Name: "Modify while iterating",
Update: func(t *testing.T, db TestDB, tx *Snapshot) (err error) {
first := true
pivot := User{Name: "User1"}
db.Users.ByName.AscendAfter(tx, &pivot, func(u *User) bool {
u.Name += "Mod"
if err = db.Users.Update(tx, u); err != nil {
return false
}
if first {
first = false
return true
}
prev, ok := db.Users.ByID.Get(tx, &User{ID: u.ID - 1})
if !ok {
err = errors.New("Previous user not found")
return false
}
if !strings.HasSuffix(prev.Name, "Mod") {
err = errors.New("Incorrect user name: " + prev.Name)
return false
}
return true
})
return nil
},
State: DBState{
UsersByID: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1Mod", Email: "user.1@x.com"},
{ID: 3, Name: "User2Mod", Email: "user.2@x.com"},
{ID: 4, Name: "User3Mod", Email: "user.3@x.com"},
},
UsersByEmail: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1Mod", Email: "user.1@x.com"},
{ID: 3, Name: "User2Mod", Email: "user.2@x.com"},
{ID: 4, Name: "User3Mod", Email: "user.3@x.com"},
},
UsersByName: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1Mod", Email: "user.1@x.com"},
{ID: 3, Name: "User2Mod", Email: "user.2@x.com"},
{ID: 4, Name: "User3Mod", Email: "user.3@x.com"},
},
},
}, {
Name: "Iterate after modifying",
Update: func(t *testing.T, db TestDB, tx *Snapshot) (err error) {
u := &User{ID: 5, Name: "User4Mod", Email: "user.4@x.com"}
if err := db.Users.Insert(tx, u); err != nil {
return err
}
first := true
db.Users.ByName.DescendAfter(tx, &User{Name: "User5Mod"}, func(u *User) bool {
u.Name = strings.TrimSuffix(u.Name, "Mod")
if err = db.Users.Update(tx, u); err != nil {
return false
}
if first {
first = false
return true
}
prev, ok := db.Users.ByID.Get(tx, &User{ID: u.ID + 1})
if !ok {
err = errors.New("Previous user not found")
return false
}
if strings.HasSuffix(prev.Name, "Mod") {
err = errors.New("Incorrect user name: " + prev.Name)
return false
}
return true
})
return nil
},
State: DBState{
UsersByID: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
{ID: 5, Name: "User4", Email: "user.4@x.com"},
},
UsersByEmail: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
{ID: 5, Name: "User4", Email: "user.4@x.com"},
},
UsersByName: []User{
{ID: 1, Name: "User0", Email: "user.0@x.com"},
{ID: 2, Name: "User1", Email: "user.1@x.com"},
{ID: 3, Name: "User2", Email: "user.2@x.com"},
{ID: 4, Name: "User3", Email: "user.3@x.com"},
{ID: 5, Name: "User4", Email: "user.4@x.com"},
},
},
}},
}}