853 lines
22 KiB
Go
853 lines
22 KiB
Go
|
package mdb
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
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 ErrNotFound
|
||
|
}
|
||
|
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: ErrDuplicate,
|
||
|
|
||
|
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: ErrDuplicate,
|
||
|
|
||
|
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: ErrDuplicate,
|
||
|
|
||
|
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: ErrDuplicate,
|
||
|
|
||
|
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: ErrReadOnly,
|
||
|
}},
|
||
|
}, {
|
||
|
|
||
|
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: ErrNotFound,
|
||
|
|
||
|
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 ErrNotFound
|
||
|
}
|
||
|
user.Name = "Bob"
|
||
|
user.Email = "b@c.com"
|
||
|
return db.Users.Update(db.Snapshot(), user)
|
||
|
},
|
||
|
|
||
|
ExpectedUpdateError: ErrReadOnly,
|
||
|
|
||
|
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: ErrDuplicate,
|
||
|
|
||
|
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 ErrNotFound
|
||
|
}
|
||
|
|
||
|
u.Email = "a@b.com"
|
||
|
return db.Users.Update(tx, u)
|
||
|
},
|
||
|
|
||
|
ExpectedUpdateError: ErrDuplicate,
|
||
|
|
||
|
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: ErrReadOnly,
|
||
|
|
||
|
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: ErrNotFound,
|
||
|
|
||
|
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 ErrNotFound
|
||
|
}
|
||
|
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 ErrNotFound
|
||
|
}
|
||
|
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"},
|
||
|
},
|
||
|
},
|
||
|
}},
|
||
|
}}
|