forked from mirror/gorm
Refact tests
This commit is contained in:
parent
6a00344355
commit
cb7d545ac0
|
@ -17,14 +17,14 @@ func SaveBeforeAssociations(scope *Scope) {
|
||||||
newDB := scope.NewDB()
|
newDB := scope.NewDB()
|
||||||
|
|
||||||
if value.CanAddr() {
|
if value.CanAddr() {
|
||||||
newDB.Save(value.Addr().Interface())
|
scope.Err(newDB.Save(value.Addr().Interface()).Error)
|
||||||
} else {
|
} else {
|
||||||
// If can't take address, then clone the value and set it back
|
// If can't take address, then clone the value and set it back
|
||||||
value = reflect.New(reflect.ValueOf(field.Value).Type()).Elem()
|
value = reflect.New(reflect.ValueOf(field.Value).Type()).Elem()
|
||||||
for _, f := range newDB.NewScope(field.Value).Fields() {
|
for _, f := range newDB.NewScope(field.Value).Fields() {
|
||||||
value.FieldByName(f.Name).Set(reflect.ValueOf(f.Value))
|
value.FieldByName(f.Name).Set(reflect.ValueOf(f.Value))
|
||||||
}
|
}
|
||||||
newDB.Save(value.Addr().Interface())
|
scope.Err(newDB.Save(value.Addr().Interface()).Error)
|
||||||
scope.SetColumn(field.Name, value.Interface())
|
scope.SetColumn(field.Name, value.Interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +50,13 @@ func SaveAfterAssociations(scope *Scope) {
|
||||||
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
newDB.Save(elem)
|
scope.Err(newDB.Save(elem).Error)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
newDB := scope.NewDB()
|
newDB := scope.NewDB()
|
||||||
if value.CanAddr() {
|
if value.CanAddr() {
|
||||||
newDB.NewScope(field.Value).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
newDB.NewScope(field.Value).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
||||||
newDB.Save(field.Value)
|
scope.Err(newDB.Save(field.Value).Error)
|
||||||
} else {
|
} else {
|
||||||
destValue := reflect.New(reflect.TypeOf(field.Value)).Elem()
|
destValue := reflect.New(reflect.TypeOf(field.Value)).Elem()
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func SaveAfterAssociations(scope *Scope) {
|
||||||
|
|
||||||
elem := destValue.Addr().Interface()
|
elem := destValue.Addr().Interface()
|
||||||
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue())
|
||||||
newDB.Save(elem)
|
scope.Err(newDB.Save(elem).Error)
|
||||||
scope.SetColumn(field.Name, destValue.Interface())
|
scope.SetColumn(field.Name, destValue.Interface())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
user := User{Name: "1", Age: 18, Birthday: time.Now(), UserNum: Num(111), PasswordHash: []byte{'f', 'a', 'k', '4'}}
|
||||||
|
|
||||||
|
if !db.NewRecord(user) || !db.NewRecord(&user) {
|
||||||
|
t.Error("User should be new record before create")
|
||||||
|
}
|
||||||
|
|
||||||
|
if count := db.Save(&user).RowsAffected; count != 1 {
|
||||||
|
t.Error("There should be one record be affected when create record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.NewRecord(user) || db.NewRecord(&user) {
|
||||||
|
t.Error("User should not new record after save")
|
||||||
|
}
|
||||||
|
|
||||||
|
var newUser User
|
||||||
|
db.First(&newUser, user.Id)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(newUser.PasswordHash, []byte{'f', 'a', 'k', '4'}) {
|
||||||
|
t.Errorf("User's PasswordHash should be saved ([]byte)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if newUser.Age != 18 {
|
||||||
|
t.Errorf("User's Age should be saved (int)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if newUser.UserNum != Num(111) {
|
||||||
|
t.Errorf("User's UserNum should be saved (custom type)")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
user1, user2 := User{Name: "delete1"}, User{Name: "delete2"}
|
||||||
|
db.Save(&user1)
|
||||||
|
db.Save(&user2)
|
||||||
|
|
||||||
|
if db.Delete(&user1).Error != nil {
|
||||||
|
t.Errorf("No error should happen when delete a record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !db.Where("name = ?", user1.Name).First(&User{}).RecordNotFound() {
|
||||||
|
t.Errorf("User can't be found after delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Where("name = ?", user2.Name).First(&User{}).RecordNotFound() {
|
||||||
|
t.Errorf("Other users that not deleted should be found-able")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInlineDelete(t *testing.T) {
|
||||||
|
user1, user2 := User{Name: "inline_delete1"}, User{Name: "inline_delete2"}
|
||||||
|
db.Save(&user1)
|
||||||
|
db.Save(&user2)
|
||||||
|
|
||||||
|
if db.Delete(&User{}, user1.Id).Error != nil {
|
||||||
|
t.Errorf("No error should happen when delete a record")
|
||||||
|
} else if !db.Where("name = ?", user1.Name).First(&User{}).RecordNotFound() {
|
||||||
|
t.Errorf("User can't be found after delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Delete(&User{}, "name = ?", user2.Name).Error != nil {
|
||||||
|
t.Errorf("No error should happen when delete a record")
|
||||||
|
} else if !db.Where("name = ?", user2.Name).First(&User{}).RecordNotFound() {
|
||||||
|
t.Errorf("User can't be found after delete")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoftDelete(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
Id int64
|
||||||
|
Name string
|
||||||
|
DeletedAt time.Time
|
||||||
|
}
|
||||||
|
db.AutoMigrate(&User{})
|
||||||
|
|
||||||
|
user := User{Name: "soft_delete"}
|
||||||
|
db.Save(&user)
|
||||||
|
db.Delete(&user)
|
||||||
|
|
||||||
|
if db.First(&User{}, "name = ?", user.Name).Error == nil {
|
||||||
|
t.Errorf("Can't find a soft deleted record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Unscoped().First(&User{}, "name = ?", user.Name).Error != nil {
|
||||||
|
t.Errorf("Should be able to find soft deleted record with Unscoped")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Unscoped().Delete(&user)
|
||||||
|
if !db.Unscoped().First(&User{}, "name = ?", user.Name).RecordNotFound() {
|
||||||
|
t.Errorf("Can't find permanently deleted record")
|
||||||
|
}
|
||||||
|
}
|
3516
main_test.go
3516
main_test.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestQueryManyToManyWithRelated(t *testing.T) {
|
||||||
|
db.Model(&User{}).Related(&[]Language{}, "Languages")
|
||||||
|
// SELECT `languages`.* FROM `languages` INNER JOIN `user_languages` ON `languages`.`id` = `user_languages`.`language_id` WHERE `user_languages`.`user_id` = 111
|
||||||
|
// db.Model(&User{}).Many2Many("Languages").Find(&[]Language{})
|
||||||
|
// db.Model(&User{}).Many2Many("Languages").Add(&Language{})
|
||||||
|
// db.Model(&User{}).Many2Many("Languages").Remove(&Language{})
|
||||||
|
// db.Model(&User{}).Many2Many("Languages").Replace(&[]Language{})
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runMigration() {
|
||||||
|
if err := db.DropTable(&User{}).Error; err != nil {
|
||||||
|
fmt.Printf("Got error when try to delete table users, %+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Exec("drop table products;")
|
||||||
|
db.Exec("drop table emails;")
|
||||||
|
db.Exec("drop table addresses")
|
||||||
|
db.Exec("drop table credit_cards")
|
||||||
|
db.Exec("drop table roles")
|
||||||
|
db.Exec("drop table companies")
|
||||||
|
db.Exec("drop table animals")
|
||||||
|
db.Exec("drop table user_companies")
|
||||||
|
|
||||||
|
if err := db.CreateTable(&Animal{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.CreateTable(&User{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.CreateTable(&Product{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.CreateTable(Email{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(Address{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(&CreditCard{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(Company{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(Role{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(UserCompany{}).Error; err != nil {
|
||||||
|
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndexes(t *testing.T) {
|
||||||
|
if err := db.Model(&Email{}).AddIndex("idx_email_email", "email").Error; err != nil {
|
||||||
|
t.Errorf("Got error when tried to create index: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&Email{}).RemoveIndex("idx_email_email").Error; err != nil {
|
||||||
|
t.Errorf("Got error when tried to remove index: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&Email{}).AddIndex("idx_email_email_and_user_id", "user_id", "email").Error; err != nil {
|
||||||
|
t.Errorf("Got error when tried to create index: %+v", err)
|
||||||
|
}
|
||||||
|
if err := db.Model(&Email{}).RemoveIndex("idx_email_email_and_user_id").Error; err != nil {
|
||||||
|
t.Errorf("Got error when tried to remove index: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&Email{}).AddUniqueIndex("idx_email_email_and_user_id", "user_id", "email").Error; err != nil {
|
||||||
|
t.Errorf("Got error when tried to create index: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(db.Save(&User{Name: "unique_indexes", Emails: []Email{{Email: "user1@example.comiii"}, {Email: "user1@example.com"}, {Email: "user1@example.com"}}}).Error)
|
||||||
|
if db.Debug().Save(&User{Name: "unique_indexes", Emails: []Email{{Email: "user1@example.comiii"}, {Email: "user1@example.com"}, {Email: "user1@example.com"}}}).Error == nil {
|
||||||
|
t.Errorf("Should get to create duplicate record when having unique index")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFirstAndLast(t *testing.T) {
|
||||||
|
db.Save(&User{Name: "user1", Emails: []Email{{Email: "user1@example.com"}}})
|
||||||
|
db.Save(&User{Name: "user2", Emails: []Email{{Email: "user2@example.com"}}})
|
||||||
|
|
||||||
|
var user1, user2, user3, user4 User
|
||||||
|
db.First(&user1)
|
||||||
|
db.Order("id").Find(&user2)
|
||||||
|
|
||||||
|
db.Last(&user3)
|
||||||
|
db.Order("id desc").Find(&user4)
|
||||||
|
if user1.Id != user2.Id || user3.Id != user4.Id {
|
||||||
|
t.Errorf("First and Last should by order by primary key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
db.First(&users)
|
||||||
|
if len(users) != 1 {
|
||||||
|
t.Errorf("Find first record as slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Joins("left join emails on emails.user_id = users.id").First(&User{}).Error != nil {
|
||||||
|
t.Errorf("Should not raise any error when order with Join table")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirstAndLastWithNoStdPrimaryKey(t *testing.T) {
|
||||||
|
db.Save(&Animal{Name: "animal1"})
|
||||||
|
db.Save(&Animal{Name: "animal2"})
|
||||||
|
|
||||||
|
var animal1, animal2, animal3, animal4 Animal
|
||||||
|
db.First(&animal1)
|
||||||
|
db.Order("counter").Find(&animal2)
|
||||||
|
|
||||||
|
db.Last(&animal3)
|
||||||
|
db.Order("counter desc").Find(&animal4)
|
||||||
|
if animal1.Counter != animal2.Counter || animal3.Counter != animal4.Counter {
|
||||||
|
t.Errorf("First and Last should work correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindAsSliceOfPointers(t *testing.T) {
|
||||||
|
db.Save(&User{Name: "user"})
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
db.Find(&users)
|
||||||
|
|
||||||
|
var userPointers []*User
|
||||||
|
db.Find(&userPointers)
|
||||||
|
|
||||||
|
if len(users) == 0 || len(users) != len(userPointers) {
|
||||||
|
t.Errorf("Find slice of pointers")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,224 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
Id int64
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (role *Role) Scan(value interface{}) error {
|
||||||
|
role.Name = string(value.([]uint8))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (role Role) Value() (driver.Value, error) {
|
||||||
|
return role.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (role Role) IsAdmin() bool {
|
||||||
|
return role.Name == "admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Language struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ignored struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Num int64
|
||||||
|
|
||||||
|
func (i *Num) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
case int64:
|
||||||
|
*i = Num(s)
|
||||||
|
default:
|
||||||
|
return errors.New("Cannot scan NamedInt from " + reflect.ValueOf(src).String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id int64
|
||||||
|
Age int64
|
||||||
|
UserNum Num
|
||||||
|
Name string `sql:"size:255"`
|
||||||
|
Birthday time.Time // Time
|
||||||
|
CreatedAt time.Time // CreatedAt: Time of record is created, will be insert automatically
|
||||||
|
UpdatedAt time.Time // UpdatedAt: Time of record is updated, will be updated automatically
|
||||||
|
Emails []Email // Embedded structs
|
||||||
|
Ignored Ignored `sql:"-"`
|
||||||
|
BillingAddress Address // Embedded struct
|
||||||
|
BillingAddressId sql.NullInt64 // Embedded struct's foreign key
|
||||||
|
ShippingAddress Address // Embedded struct
|
||||||
|
ShippingAddressId int64 // Embedded struct's foreign key
|
||||||
|
CreditCard CreditCard
|
||||||
|
Latitude float64
|
||||||
|
CompanyId int64
|
||||||
|
Company
|
||||||
|
Role
|
||||||
|
PasswordHash []byte
|
||||||
|
IgnoreMe int64 `sql:"-"`
|
||||||
|
IgnoreStringSlice []string `sql:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserCompany struct {
|
||||||
|
Id int64
|
||||||
|
UserId int64
|
||||||
|
CompanyId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t UserCompany) TableName() string {
|
||||||
|
return "user_companies"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreditCard struct {
|
||||||
|
Id int8
|
||||||
|
Number string
|
||||||
|
UserId sql.NullInt64
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
Id int16
|
||||||
|
UserId int
|
||||||
|
Email string `sql:"type:varchar(100); unique"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Id int
|
||||||
|
Address1 string
|
||||||
|
Address2 string
|
||||||
|
Post string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
Id int64
|
||||||
|
Code string
|
||||||
|
Price int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
AfterFindCallTimes int64
|
||||||
|
BeforeCreateCallTimes int64
|
||||||
|
AfterCreateCallTimes int64
|
||||||
|
BeforeUpdateCallTimes int64
|
||||||
|
AfterUpdateCallTimes int64
|
||||||
|
BeforeSaveCallTimes int64
|
||||||
|
AfterSaveCallTimes int64
|
||||||
|
BeforeDeleteCallTimes int64
|
||||||
|
AfterDeleteCallTimes int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Animal struct {
|
||||||
|
Counter int64 `primaryKey:"yes"`
|
||||||
|
Name string
|
||||||
|
From string //test reserved sql keyword as field name
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Details struct {
|
||||||
|
Id int64
|
||||||
|
Bulk gorm.Hstore
|
||||||
|
}
|
||||||
|
|
||||||
|
type Category struct {
|
||||||
|
Id int64
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Post struct {
|
||||||
|
Id int64
|
||||||
|
CategoryId sql.NullInt64
|
||||||
|
MainCategoryId int64
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
Comments []Comment
|
||||||
|
Category Category
|
||||||
|
MainCategory Category
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comment struct {
|
||||||
|
Id int64
|
||||||
|
PostId int64
|
||||||
|
Content string
|
||||||
|
Post Post
|
||||||
|
}
|
||||||
|
|
||||||
|
type Order struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cart struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Cart) TableName() string {
|
||||||
|
return "shopping_cart"
|
||||||
|
}
|
||||||
|
|
||||||
|
type BigEmail struct {
|
||||||
|
Id int64
|
||||||
|
UserId int64
|
||||||
|
Email string
|
||||||
|
UserAgent string
|
||||||
|
RegisteredAt time.Time
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BigEmail) TableName() string {
|
||||||
|
return "emails"
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullTime struct {
|
||||||
|
Time time.Time
|
||||||
|
Valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nt *NullTime) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
nt.Valid = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
nt.Time, nt.Valid = value.(time.Time), true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nt NullTime) Value() (driver.Value, error) {
|
||||||
|
if !nt.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nt.Time, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullValue struct {
|
||||||
|
Id int64
|
||||||
|
Name sql.NullString `sql:"not null"`
|
||||||
|
Age sql.NullInt64
|
||||||
|
Male sql.NullBool
|
||||||
|
Height sql.NullFloat64
|
||||||
|
AddedAt NullTime
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
package gorm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
product1 := Product{Code: "123"}
|
||||||
|
product2 := Product{Code: "234"}
|
||||||
|
animal1 := Animal{Name: "Ferdinand"}
|
||||||
|
animal2 := Animal{Name: "nerdz"}
|
||||||
|
|
||||||
|
db.Save(&product1).Save(&product2).Update("code", "456")
|
||||||
|
|
||||||
|
if product2.Code != "456" {
|
||||||
|
t.Errorf("Record should be updated with update attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Save(&animal1).Save(&animal2).Update("name", "Francis")
|
||||||
|
|
||||||
|
if animal2.Name != "Francis" {
|
||||||
|
t.Errorf("Record should be updated with update attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.First(&product1, product1.Id)
|
||||||
|
db.First(&product2, product2.Id)
|
||||||
|
updated_at1 := product1.UpdatedAt
|
||||||
|
updated_at2 := product2.UpdatedAt
|
||||||
|
|
||||||
|
db.First(&animal1, animal1.Counter)
|
||||||
|
db.First(&animal2, animal2.Counter)
|
||||||
|
animalUpdated_at1 := animal1.UpdatedAt
|
||||||
|
animalUpdated_at2 := animal2.UpdatedAt
|
||||||
|
|
||||||
|
var product3 Product
|
||||||
|
db.First(&product3, product2.Id).Update("code", "456")
|
||||||
|
if updated_at2.Format(time.RFC3339Nano) != product3.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should not be updated if nothing changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '123'").Error != nil {
|
||||||
|
t.Errorf("Product 123 should not be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '234'").Error == nil {
|
||||||
|
t.Errorf("Product 234 should be changed to 456")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '456'").Error != nil {
|
||||||
|
t.Errorf("Product 234 should be changed to 456")
|
||||||
|
}
|
||||||
|
|
||||||
|
var animal3 Animal
|
||||||
|
db.First(&animal3, animal2.Counter).Update("Name", "Robert")
|
||||||
|
|
||||||
|
if animalUpdated_at2.Format(time.RFC3339Nano) != animal2.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should not be updated if nothing changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'Ferdinand'").Error != nil {
|
||||||
|
t.Errorf("Animal 'Ferdinand' should not be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'nerdz'").Error == nil {
|
||||||
|
t.Errorf("Animal 'nerdz' should be changed to 'Francis'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'Robert'").Error != nil {
|
||||||
|
t.Errorf("Animal 'nerdz' should be changed to 'Robert'")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Table("products").Where("code in (?)", []string{"123"}).Update("code", "789")
|
||||||
|
|
||||||
|
var product4 Product
|
||||||
|
db.First(&product4, product1.Id)
|
||||||
|
if updated_at1.Format(time.RFC3339Nano) != product4.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should be updated if something changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '123'").Error == nil {
|
||||||
|
t.Errorf("Product 123 should be changed to 789")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '456'").Error != nil {
|
||||||
|
t.Errorf("Product 456 should not be changed to 789")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = '789'").Error != nil {
|
||||||
|
t.Errorf("Product 123 should be changed to 789")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Model(product2).Update("CreatedAt", time.Now().Add(time.Hour)).Error != nil {
|
||||||
|
t.Error("No error should raise when update with CamelCase")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Model(&product2).UpdateColumn("CreatedAt", time.Now().Add(time.Hour)).Error != nil {
|
||||||
|
t.Error("No error should raise when update_column with CamelCase")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Table("animals").Where("name in (?)", []string{"Ferdinand"}).Update("name", "Franz")
|
||||||
|
|
||||||
|
var animal4 Animal
|
||||||
|
db.First(&animal4, animal1.Counter)
|
||||||
|
if animalUpdated_at1.Format(time.RFC3339Nano) != animal4.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("animalUpdated_at should be updated if something changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'Ferdinand'").Error == nil {
|
||||||
|
t.Errorf("Animal 'Ferdinand' should be changed to 'Franz'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'Robert'").Error != nil {
|
||||||
|
t.Errorf("Animal 'Robert' should not be changed to 'Francis'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Animal{}, "name = 'Franz'").Error != nil {
|
||||||
|
t.Errorf("Animal 'nerdz' should be changed to 'Franz'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Model(animal2).Update("CreatedAt", time.Now().Add(time.Hour)).Error != nil {
|
||||||
|
t.Error("No error should raise when update with CamelCase")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Model(&animal2).UpdateColumn("CreatedAt", time.Now().Add(time.Hour)).Error != nil {
|
||||||
|
t.Error("No error should raise when update_column with CamelCase")
|
||||||
|
}
|
||||||
|
|
||||||
|
var animals []Animal
|
||||||
|
db.Find(&animals)
|
||||||
|
if count := db.Model(Animal{}).Update("CreatedAt", time.Now().Add(2*time.Hour)).RowsAffected; count != int64(len(animals)) {
|
||||||
|
t.Error("RowsAffected should be correct when do batch update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdates(t *testing.T) {
|
||||||
|
product1 := Product{Code: "abc", Price: 10}
|
||||||
|
product2 := Product{Code: "cde", Price: 20}
|
||||||
|
db.Save(&product1).Save(&product2)
|
||||||
|
db.Model(&product2).Updates(map[string]interface{}{"code": "edf", "price": 100})
|
||||||
|
if product2.Code != "edf" || product2.Price != 100 {
|
||||||
|
t.Errorf("Record should be updated also with update attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.First(&product1, product1.Id)
|
||||||
|
db.First(&product2, product2.Id)
|
||||||
|
updated_at1 := product1.UpdatedAt
|
||||||
|
updated_at2 := product2.UpdatedAt
|
||||||
|
|
||||||
|
var product3 Product
|
||||||
|
db.First(&product3, product2.Id).Updates(Product{Code: "edf", Price: 100})
|
||||||
|
if product3.Code != "edf" || product3.Price != 100 {
|
||||||
|
t.Errorf("Record should be updated with update attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated_at2.Format(time.RFC3339Nano) != product3.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should not be updated if nothing changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = 'abc' and price = 10").Error != nil {
|
||||||
|
t.Errorf("Product abc should not be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = 'cde'").Error == nil {
|
||||||
|
t.Errorf("Product cde should be renamed to edf")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = 'edf' and price = 100").Error != nil {
|
||||||
|
t.Errorf("Product cde should be renamed to edf")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Table("products").Where("code in (?)", []string{"abc"}).Updates(map[string]string{"code": "fgh", "price": "200"})
|
||||||
|
if db.First(&Product{}, "code = 'abc'").Error == nil {
|
||||||
|
t.Errorf("Product abc's code should be changed to fgh")
|
||||||
|
}
|
||||||
|
|
||||||
|
var product4 Product
|
||||||
|
db.First(&product4, product1.Id)
|
||||||
|
if updated_at1.Format(time.RFC3339Nano) != product4.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should be updated if something changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = 'edf' and price = ?", 100).Error != nil {
|
||||||
|
t.Errorf("Product cde's code should not be changed to fgh")
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.First(&Product{}, "code = 'fgh' and price = 200").Error != nil {
|
||||||
|
t.Errorf("We should have Product fgh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateColumn(t *testing.T) {
|
||||||
|
product1 := Product{Code: "update_column 1", Price: 10}
|
||||||
|
product2 := Product{Code: "update_column 2", Price: 20}
|
||||||
|
db.Save(&product1).Save(&product2).UpdateColumn(map[string]interface{}{"code": "update_column 3", "price": 100})
|
||||||
|
if product2.Code != "update_column 3" || product2.Price != 100 {
|
||||||
|
t.Errorf("product 2 should be updated with update column")
|
||||||
|
}
|
||||||
|
|
||||||
|
var product3 Product
|
||||||
|
db.First(&product3, product1.Id)
|
||||||
|
if product3.Code != "update_column 1" || product3.Price != 10 {
|
||||||
|
t.Errorf("product 1 should not be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
var product4, product5 Product
|
||||||
|
db.First(&product4, product2.Id)
|
||||||
|
updated_at1 := product4.UpdatedAt
|
||||||
|
|
||||||
|
db.Model(Product{}).Where(product2.Id).UpdateColumn("code", "update_column_new")
|
||||||
|
db.First(&product5, product2.Id)
|
||||||
|
if updated_at1.Format(time.RFC3339Nano) != product5.UpdatedAt.Format(time.RFC3339Nano) {
|
||||||
|
t.Errorf("updated_at should not be updated with update column")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue