2014-01-28 12:21:02 +04:00
package gorm_test
2013-10-25 18:31:56 +04:00
2019-06-13 06:21:13 +03:00
// Run tests
// $ docker-compose up
// $ ./test_all.sh
2013-10-26 20:36:56 +04:00
import (
2019-06-10 15:33:20 +03:00
"context"
2014-07-29 08:32:58 +04:00
"database/sql"
"database/sql/driver"
2019-11-19 11:08:00 +03:00
"errors"
2013-10-29 03:39:26 +04:00
"fmt"
2016-03-05 13:54:59 +03:00
"os"
2016-05-28 19:49:32 +03:00
"path/filepath"
2016-02-14 18:29:06 +03:00
"reflect"
2014-08-29 09:51:45 +04:00
"strconv"
2018-05-02 17:37:51 +03:00
"strings"
2019-04-14 10:38:06 +03:00
"sync"
2016-03-05 13:54:59 +03:00
"testing"
"time"
2014-08-29 09:51:45 +04:00
2016-03-05 13:54:59 +03:00
"github.com/erikstmartin/go-testdb"
2014-11-25 10:20:21 +03:00
"github.com/jinzhu/gorm"
2016-03-05 13:54:59 +03:00
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
2014-07-29 08:32:58 +04:00
"github.com/jinzhu/now"
2013-10-26 20:36:56 +04:00
)
2013-10-25 18:31:56 +04:00
2013-10-27 03:38:08 +04:00
var (
2016-01-10 16:26:55 +03:00
DB * gorm . DB
2013-10-27 03:38:08 +04:00
t1 , t2 , t3 , t4 , t5 time . Time
)
2013-10-26 17:05:54 +04:00
func init ( ) {
2013-10-29 03:39:26 +04:00
var err error
2015-08-09 00:08:25 +03:00
if DB , err = OpenTestConnection ( ) ; err != nil {
panic ( fmt . Sprintf ( "No error should happen when connecting to test database, but got err=%+v" , err ) )
}
runMigration ( )
}
2016-01-10 16:26:55 +03:00
func OpenTestConnection ( ) ( db * gorm . DB , err error ) {
2018-02-10 06:06:43 +03:00
dbDSN := os . Getenv ( "GORM_DSN" )
2013-12-04 08:06:50 +04:00
switch os . Getenv ( "GORM_DIALECT" ) {
2014-09-16 19:32:35 +04:00
case "mysql" :
fmt . Println ( "testing mysql..." )
2018-02-10 06:06:43 +03:00
if dbDSN == "" {
2019-06-13 06:21:13 +03:00
dbDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True"
2016-05-21 21:05:31 +03:00
}
2018-02-10 06:06:43 +03:00
db , err = gorm . Open ( "mysql" , dbDSN )
2014-09-16 19:32:35 +04:00
case "postgres" :
fmt . Println ( "testing postgres..." )
2018-02-10 06:06:43 +03:00
if dbDSN == "" {
2019-06-13 06:21:13 +03:00
dbDSN = "user=gorm password=gorm DB.name=gorm port=9920 sslmode=disable"
2016-05-21 21:05:31 +03:00
}
2018-02-10 06:06:43 +03:00
db , err = gorm . Open ( "postgres" , dbDSN )
2014-09-16 19:32:35 +04:00
case "mssql" :
2017-03-22 13:01:29 +03:00
// CREATE LOGIN gorm WITH PASSWORD = 'LoremIpsum86';
// CREATE DATABASE gorm;
// USE gorm;
// CREATE USER gorm FROM LOGIN gorm;
// sp_changedbowner 'gorm';
2014-09-16 19:32:35 +04:00
fmt . Println ( "testing mssql..." )
2018-02-10 06:06:43 +03:00
if dbDSN == "" {
2019-06-13 06:21:13 +03:00
dbDSN = "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
2018-02-10 06:06:43 +03:00
}
db , err = gorm . Open ( "mssql" , dbDSN )
2014-09-16 19:32:35 +04:00
default :
fmt . Println ( "testing sqlite3..." )
2016-05-28 19:49:32 +03:00
db , err = gorm . Open ( "sqlite3" , filepath . Join ( os . TempDir ( ) , "gorm.db" ) )
2013-10-29 03:39:26 +04:00
}
2016-08-07 13:00:06 +03:00
// db.SetLogger(Logger{log.New(os.Stdout, "\r\n", 0)})
// db.SetLogger(log.New(os.Stdout, "\r\n", 0))
2018-02-10 03:24:39 +03:00
if debug := os . Getenv ( "DEBUG" ) ; debug == "true" {
2016-08-07 13:00:06 +03:00
db . LogMode ( true )
2018-02-10 03:24:39 +03:00
} else if debug == "false" {
db . LogMode ( false )
2016-08-07 13:00:06 +03:00
}
db . DB ( ) . SetMaxIdleConns ( 10 )
2015-08-09 00:08:25 +03:00
return
2014-07-29 06:59:13 +04:00
}
2018-05-02 17:37:51 +03:00
func TestOpen_ReturnsError_WithBadArgs ( t * testing . T ) {
stringRef := "foo"
testCases := [ ] interface { } { 42 , time . Now ( ) , & stringRef }
for _ , tc := range testCases {
t . Run ( fmt . Sprintf ( "%v" , tc ) , func ( t * testing . T ) {
_ , err := gorm . Open ( "postgresql" , tc )
if err == nil {
t . Error ( "Should got error with invalid database source" )
}
if ! strings . HasPrefix ( err . Error ( ) , "invalid database source:" ) {
t . Errorf ( "Should got error starting with \"invalid database source:\", but got %q" , err . Error ( ) )
}
} )
}
}
2015-06-01 06:04:11 +03:00
func TestStringPrimaryKey ( t * testing . T ) {
type UUIDStruct struct {
ID string ` gorm:"primary_key" `
Name string
}
2016-09-07 16:54:19 +03:00
DB . DropTable ( & UUIDStruct { } )
2015-06-01 06:17:51 +03:00
DB . AutoMigrate ( & UUIDStruct { } )
2015-06-01 06:04:11 +03:00
data := UUIDStruct { ID : "uuid" , Name : "hello" }
2016-09-07 16:54:19 +03:00
if err := DB . Save ( & data ) . Error ; err != nil || data . ID != "uuid" || data . Name != "hello" {
t . Errorf ( "string primary key should not be populated" )
}
data = UUIDStruct { ID : "uuid" , Name : "hello world" }
if err := DB . Save ( & data ) . Error ; err != nil || data . ID != "uuid" || data . Name != "hello world" {
2015-06-01 06:04:11 +03:00
t . Errorf ( "string primary key should not be populated" )
}
}
2014-07-29 08:32:58 +04:00
func TestExceptionsWithInvalidSql ( t * testing . T ) {
var columns [ ] string
2014-08-28 11:33:43 +04:00
if DB . Where ( "sdsd.zaaa = ?" , "sd;;;aa" ) . Pluck ( "aaa" , & columns ) . Error == nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should got error with invalid SQL" )
}
2014-08-28 11:33:43 +04:00
if DB . Model ( & User { } ) . Where ( "sdsd.zaaa = ?" , "sd;;;aa" ) . Pluck ( "aaa" , & columns ) . Error == nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should got error with invalid SQL" )
}
2014-08-28 11:33:43 +04:00
if DB . Where ( "sdsd.zaaa = ?" , "sd;;;aa" ) . Find ( & User { } ) . Error == nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should got error with invalid SQL" )
}
var count1 , count2 int64
2014-08-28 11:33:43 +04:00
DB . Model ( & User { } ) . Count ( & count1 )
2014-07-29 08:32:58 +04:00
if count1 <= 0 {
t . Errorf ( "Should find some users" )
}
2014-08-28 11:33:43 +04:00
if DB . Where ( "name = ?" , "jinzhu; delete * from users" ) . First ( & User { } ) . Error == nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should got error with invalid SQL" )
}
2014-08-28 11:33:43 +04:00
DB . Model ( & User { } ) . Count ( & count2 )
2014-07-29 08:32:58 +04:00
if count1 != count2 {
t . Errorf ( "No user should not be deleted by invalid SQL" )
}
}
func TestSetTable ( t * testing . T ) {
2015-03-12 12:47:31 +03:00
DB . Create ( getPreparedUser ( "pluck_user1" , "pluck_user" ) )
DB . Create ( getPreparedUser ( "pluck_user2" , "pluck_user" ) )
DB . Create ( getPreparedUser ( "pluck_user3" , "pluck_user" ) )
if err := DB . Table ( "users" ) . Where ( "role = ?" , "pluck_user" ) . Pluck ( "age" , & [ ] int { } ) . Error ; err != nil {
2016-01-15 16:03:35 +03:00
t . Error ( "No errors should happen if set table for pluck" , err )
2014-07-29 08:32:58 +04:00
}
var users [ ] User
2014-08-28 11:33:43 +04:00
if DB . Table ( "users" ) . Find ( & [ ] User { } ) . Error != nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "No errors should happen if set table for find" )
}
2014-08-28 11:33:43 +04:00
if DB . Table ( "invalid_table" ) . Find ( & users ) . Error == nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should got error when table is set to an invalid table" )
}
2014-08-28 11:33:43 +04:00
DB . Exec ( "drop table deleted_users;" )
if DB . Table ( "deleted_users" ) . CreateTable ( & User { } ) . Error != nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Create table with specified table" )
}
2014-08-28 11:33:43 +04:00
DB . Table ( "deleted_users" ) . Save ( & User { Name : "DeletedUser" } )
2014-07-29 08:32:58 +04:00
var deletedUsers [ ] User
2014-08-28 11:33:43 +04:00
DB . Table ( "deleted_users" ) . Find ( & deletedUsers )
2014-07-29 08:32:58 +04:00
if len ( deletedUsers ) != 1 {
t . Errorf ( "Query from specified table" )
}
2019-06-11 12:30:14 +03:00
var user User
DB . Table ( "deleted_users" ) . First ( & user , "name = ?" , "DeletedUser" )
user . Age = 20
DB . Table ( "deleted_users" ) . Save ( & user )
if DB . Table ( "deleted_users" ) . First ( & user , "name = ? AND age = ?" , "DeletedUser" , 20 ) . RecordNotFound ( ) {
t . Errorf ( "Failed to found updated user" )
}
2015-03-12 12:47:31 +03:00
DB . Save ( getPreparedUser ( "normal_user" , "reset_table" ) )
DB . Table ( "deleted_users" ) . Save ( getPreparedUser ( "deleted_user" , "reset_table" ) )
2014-07-29 08:32:58 +04:00
var user1 , user2 , user3 User
2015-03-12 12:47:31 +03:00
DB . Where ( "role = ?" , "reset_table" ) . First ( & user1 ) . Table ( "deleted_users" ) . First ( & user2 ) . Table ( "" ) . First ( & user3 )
if ( user1 . Name != "normal_user" ) || ( user2 . Name != "deleted_user" ) || ( user3 . Name != "normal_user" ) {
2014-07-29 08:32:58 +04:00
t . Errorf ( "unset specified table with blank string" )
}
}
2014-07-29 12:25:38 +04:00
type Order struct {
}
type Cart struct {
}
func ( c Cart ) TableName ( ) string {
return "shopping_cart"
}
2014-08-29 09:51:45 +04:00
func TestHasTable ( t * testing . T ) {
type Foo struct {
Id int
Stuff string
}
2014-08-29 18:53:12 +04:00
DB . DropTable ( & Foo { } )
2016-02-13 15:28:42 +03:00
// Table should not exist at this point, HasTable should return false
if ok := DB . HasTable ( "foos" ) ; ok {
t . Errorf ( "Table should not exist, but does" )
}
2014-08-29 13:12:12 +04:00
if ok := DB . HasTable ( & Foo { } ) ; ok {
2014-08-29 09:51:45 +04:00
t . Errorf ( "Table should not exist, but does" )
}
2016-02-13 15:28:42 +03:00
// We create the table
2014-08-29 18:53:12 +04:00
if err := DB . CreateTable ( & Foo { } ) . Error ; err != nil {
2014-08-29 09:51:45 +04:00
t . Errorf ( "Table should be created" )
}
2016-02-13 15:28:42 +03:00
// And now it should exits, and HasTable should return true
if ok := DB . HasTable ( "foos" ) ; ! ok {
t . Errorf ( "Table should exist, but HasTable informs it does not" )
}
2014-08-29 13:12:12 +04:00
if ok := DB . HasTable ( & Foo { } ) ; ! ok {
2014-08-29 09:51:45 +04:00
t . Errorf ( "Table should exist, but HasTable informs it does not" )
}
}
2014-07-29 08:32:58 +04:00
func TestTableName ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
DB := DB . Model ( "" )
if DB . NewScope ( Order { } ) . TableName ( ) != "orders" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Order's table name should be orders" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & Order { } ) . TableName ( ) != "orders" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&Order's table name should be orders" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( [ ] Order { } ) . TableName ( ) != "orders" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "[]Order's table name should be orders" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & [ ] Order { } ) . TableName ( ) != "orders" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&[]Order's table name should be orders" )
}
2014-08-28 11:33:43 +04:00
DB . SingularTable ( true )
if DB . NewScope ( Order { } ) . TableName ( ) != "order" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Order's singular table name should be order" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & Order { } ) . TableName ( ) != "order" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&Order's singular table name should be order" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( [ ] Order { } ) . TableName ( ) != "order" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "[]Order's singular table name should be order" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & [ ] Order { } ) . TableName ( ) != "order" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&[]Order's singular table name should be order" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & Cart { } ) . TableName ( ) != "shopping_cart" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&Cart's singular table name should be shopping_cart" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( Cart { } ) . TableName ( ) != "shopping_cart" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Cart's singular table name should be shopping_cart" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( & [ ] Cart { } ) . TableName ( ) != "shopping_cart" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "&[]Cart's singular table name should be shopping_cart" )
}
2014-08-28 11:33:43 +04:00
if DB . NewScope ( [ ] Cart { } ) . TableName ( ) != "shopping_cart" {
2014-07-29 08:32:58 +04:00
t . Errorf ( "[]Cart's singular table name should be shopping_cart" )
}
2014-08-28 11:33:43 +04:00
DB . SingularTable ( false )
2014-07-29 08:32:58 +04:00
}
2019-04-14 10:38:06 +03:00
func TestTableNameConcurrently ( t * testing . T ) {
DB := DB . Model ( "" )
if DB . NewScope ( Order { } ) . TableName ( ) != "orders" {
t . Errorf ( "Order's table name should be orders" )
}
var wg sync . WaitGroup
wg . Add ( 10 )
for i := 1 ; i <= 10 ; i ++ {
go func ( db * gorm . DB ) {
DB . SingularTable ( true )
wg . Done ( )
} ( DB )
}
wg . Wait ( )
if DB . NewScope ( Order { } ) . TableName ( ) != "order" {
t . Errorf ( "Order's singular table name should be order" )
}
DB . SingularTable ( false )
}
2016-01-04 16:49:04 +03:00
func TestNullValues ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
DB . DropTable ( & NullValue { } )
DB . AutoMigrate ( & NullValue { } )
2014-07-29 08:32:58 +04:00
2016-01-04 13:40:06 +03:00
if err := DB . Save ( & NullValue {
Name : sql . NullString { String : "hello" , Valid : true } ,
Gender : & sql . NullString { String : "M" , Valid : true } ,
2014-07-29 08:32:58 +04:00
Age : sql . NullInt64 { Int64 : 18 , Valid : true } ,
Male : sql . NullBool { Bool : true , Valid : true } ,
Height : sql . NullFloat64 { Float64 : 100.11 , Valid : true } ,
AddedAt : NullTime { Time : time . Now ( ) , Valid : true } ,
} ) . Error ; err != nil {
t . Errorf ( "Not error should raise when test null value" )
}
var nv NullValue
2014-08-28 11:33:43 +04:00
DB . First ( & nv , "name = ?" , "hello" )
2014-07-29 08:32:58 +04:00
2016-01-04 13:40:06 +03:00
if nv . Name . String != "hello" || nv . Gender . String != "M" || nv . Age . Int64 != 18 || nv . Male . Bool != true || nv . Height . Float64 != 100.11 || nv . AddedAt . Valid != true {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should be able to fetch null value" )
}
2016-01-04 13:40:06 +03:00
if err := DB . Save ( & NullValue {
Name : sql . NullString { String : "hello-2" , Valid : true } ,
Gender : & sql . NullString { String : "F" , Valid : true } ,
2014-07-29 08:32:58 +04:00
Age : sql . NullInt64 { Int64 : 18 , Valid : false } ,
Male : sql . NullBool { Bool : true , Valid : true } ,
Height : sql . NullFloat64 { Float64 : 100.11 , Valid : true } ,
AddedAt : NullTime { Time : time . Now ( ) , Valid : false } ,
} ) . Error ; err != nil {
t . Errorf ( "Not error should raise when test null value" )
}
var nv2 NullValue
2014-08-28 11:33:43 +04:00
DB . First ( & nv2 , "name = ?" , "hello-2" )
2016-01-04 13:40:06 +03:00
if nv2 . Name . String != "hello-2" || nv2 . Gender . String != "F" || nv2 . Age . Int64 != 0 || nv2 . Male . Bool != true || nv2 . Height . Float64 != 100.11 || nv2 . AddedAt . Valid != false {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should be able to fetch null value" )
}
2016-01-04 13:40:06 +03:00
if err := DB . Save ( & NullValue {
Name : sql . NullString { String : "hello-3" , Valid : false } ,
Gender : & sql . NullString { String : "M" , Valid : true } ,
2014-07-29 08:32:58 +04:00
Age : sql . NullInt64 { Int64 : 18 , Valid : false } ,
Male : sql . NullBool { Bool : true , Valid : true } ,
Height : sql . NullFloat64 { Float64 : 100.11 , Valid : true } ,
AddedAt : NullTime { Time : time . Now ( ) , Valid : false } ,
} ) . Error ; err == nil {
t . Errorf ( "Can't save because of name can't be null" )
}
}
2016-01-04 16:49:04 +03:00
func TestNullValuesWithFirstOrCreate ( t * testing . T ) {
var nv1 = NullValue {
Name : sql . NullString { String : "first_or_create" , Valid : true } ,
Gender : & sql . NullString { String : "M" , Valid : true } ,
}
var nv2 NullValue
2016-10-21 06:30:17 +03:00
result := DB . Where ( nv1 ) . FirstOrCreate ( & nv2 )
if result . RowsAffected != 1 {
t . Errorf ( "RowsAffected should be 1 after create some record" )
}
if result . Error != nil {
t . Errorf ( "Should not raise any error, but got %v" , result . Error )
2016-01-04 16:49:04 +03:00
}
if nv2 . Name . String != "first_or_create" || nv2 . Gender . String != "M" {
t . Errorf ( "first or create with nullvalues" )
}
if err := DB . Where ( nv1 ) . Assign ( NullValue { Age : sql . NullInt64 { Int64 : 18 , Valid : true } } ) . FirstOrCreate ( & nv2 ) . Error ; err != nil {
t . Errorf ( "Should not raise any error, but got %v" , err )
}
if nv2 . Age . Int64 != 18 {
t . Errorf ( "should update age to 18" )
}
}
2014-07-29 08:32:58 +04:00
func TestTransaction ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
tx := DB . Begin ( )
2014-07-29 08:32:58 +04:00
u := User { Name : "transcation" }
if err := tx . Save ( & u ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx . First ( & User { } , "name = ?" , "transcation" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
if sqlTx , ok := tx . CommonDB ( ) . ( * sql . Tx ) ; ! ok || sqlTx == nil {
t . Errorf ( "Should return the underlying sql.Tx" )
}
tx . Rollback ( )
if err := tx . First ( & User { } , "name = ?" , "transcation" ) . Error ; err == nil {
t . Errorf ( "Should not find record after rollback" )
}
2014-08-28 11:33:43 +04:00
tx2 := DB . Begin ( )
2014-07-29 08:32:58 +04:00
u2 := User { Name : "transcation-2" }
if err := tx2 . Save ( & u2 ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx2 . First ( & User { } , "name = ?" , "transcation-2" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
tx2 . Commit ( )
2014-08-28 11:33:43 +04:00
if err := DB . First ( & User { } , "name = ?" , "transcation-2" ) . Error ; err != nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Should be able to find committed record" )
}
2019-06-10 15:19:39 +03:00
tx3 := DB . Begin ( )
u3 := User { Name : "transcation-3" }
if err := tx3 . Save ( & u3 ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx3 . First ( & User { } , "name = ?" , "transcation-3" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
tx3 . RollbackUnlessCommitted ( )
if err := tx . First ( & User { } , "name = ?" , "transcation" ) . Error ; err == nil {
t . Errorf ( "Should not find record after rollback" )
}
tx4 := DB . Begin ( )
u4 := User { Name : "transcation-4" }
if err := tx4 . Save ( & u4 ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx4 . First ( & User { } , "name = ?" , "transcation-4" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
tx4 . Commit ( )
tx4 . RollbackUnlessCommitted ( )
if err := DB . First ( & User { } , "name = ?" , "transcation-4" ) . Error ; err != nil {
t . Errorf ( "Should be able to find committed record" )
}
2014-07-29 08:32:58 +04:00
}
2019-12-05 12:54:32 +03:00
func assertPanic ( t * testing . T , f func ( ) ) {
defer func ( ) {
if r := recover ( ) ; r == nil {
t . Errorf ( "The code did not panic" )
}
} ( )
f ( )
}
2019-11-19 11:08:00 +03:00
func TestTransactionWithBlock ( t * testing . T ) {
// rollback
err := DB . Transaction ( func ( tx * gorm . DB ) error {
u := User { Name : "transcation" }
if err := tx . Save ( & u ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx . First ( & User { } , "name = ?" , "transcation" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
return errors . New ( "the error message" )
} )
if err . Error ( ) != "the error message" {
t . Errorf ( "Transaction return error will equal the block returns error" )
}
if err := DB . First ( & User { } , "name = ?" , "transcation" ) . Error ; err == nil {
t . Errorf ( "Should not find record after rollback" )
}
// commit
DB . Transaction ( func ( tx * gorm . DB ) error {
u2 := User { Name : "transcation-2" }
if err := tx . Save ( & u2 ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx . First ( & User { } , "name = ?" , "transcation-2" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
return nil
} )
if err := DB . First ( & User { } , "name = ?" , "transcation-2" ) . Error ; err != nil {
t . Errorf ( "Should be able to find committed record" )
}
// panic will rollback
2019-12-05 12:54:32 +03:00
assertPanic ( t , func ( ) {
DB . Transaction ( func ( tx * gorm . DB ) error {
u3 := User { Name : "transcation-3" }
if err := tx . Save ( & u3 ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
2019-11-19 11:08:00 +03:00
2019-12-05 12:54:32 +03:00
if err := tx . First ( & User { } , "name = ?" , "transcation-3" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
2019-11-19 11:08:00 +03:00
2019-12-05 12:54:32 +03:00
panic ( "force panic" )
} )
2019-11-19 11:08:00 +03:00
} )
if err := DB . First ( & User { } , "name = ?" , "transcation" ) . Error ; err == nil {
t . Errorf ( "Should not find record after panic rollback" )
}
}
2019-06-10 15:14:44 +03:00
func TestTransaction_NoErrorOnRollbackAfterCommit ( t * testing . T ) {
tx := DB . Begin ( )
u := User { Name : "transcation" }
if err := tx . Save ( & u ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
if err := tx . Commit ( ) . Error ; err != nil {
t . Errorf ( "Commit should not raise error" )
}
if err := tx . Rollback ( ) . Error ; err != nil {
t . Errorf ( "Rollback should not raise error" )
}
}
2019-06-10 15:33:20 +03:00
func TestTransactionReadonly ( t * testing . T ) {
dialect := os . Getenv ( "GORM_DIALECT" )
if dialect == "" {
dialect = "sqlite"
}
switch dialect {
case "mssql" , "sqlite" :
t . Skipf ( "%s does not support readonly transactions\n" , dialect )
}
tx := DB . Begin ( )
u := User { Name : "transcation" }
if err := tx . Save ( & u ) . Error ; err != nil {
t . Errorf ( "No error should raise" )
}
tx . Commit ( )
tx = DB . BeginTx ( context . Background ( ) , & sql . TxOptions { ReadOnly : true } )
if err := tx . First ( & User { } , "name = ?" , "transcation" ) . Error ; err != nil {
t . Errorf ( "Should find saved record" )
}
if sqlTx , ok := tx . CommonDB ( ) . ( * sql . Tx ) ; ! ok || sqlTx == nil {
t . Errorf ( "Should return the underlying sql.Tx" )
}
u = User { Name : "transcation-2" }
if err := tx . Save ( & u ) . Error ; err == nil {
t . Errorf ( "Error should have been raised in a readonly transaction" )
}
tx . Rollback ( )
}
2014-07-29 08:32:58 +04:00
func TestRow ( t * testing . T ) {
2016-10-19 07:20:45 +03:00
user1 := User { Name : "RowUser1" , Age : 1 , Birthday : parseTime ( "2000-1-1" ) }
user2 := User { Name : "RowUser2" , Age : 10 , Birthday : parseTime ( "2010-1-1" ) }
user3 := User { Name : "RowUser3" , Age : 20 , Birthday : parseTime ( "2020-1-1" ) }
2014-08-28 11:33:43 +04:00
DB . Save ( & user1 ) . Save ( & user2 ) . Save ( & user3 )
2014-07-29 08:32:58 +04:00
2014-08-28 11:33:43 +04:00
row := DB . Table ( "users" ) . Where ( "name = ?" , user2 . Name ) . Select ( "age" ) . Row ( )
2014-07-29 08:32:58 +04:00
var age int64
row . Scan ( & age )
if age != 10 {
t . Errorf ( "Scan with Row" )
}
}
func TestRows ( t * testing . T ) {
2016-10-19 07:20:45 +03:00
user1 := User { Name : "RowsUser1" , Age : 1 , Birthday : parseTime ( "2000-1-1" ) }
user2 := User { Name : "RowsUser2" , Age : 10 , Birthday : parseTime ( "2010-1-1" ) }
user3 := User { Name : "RowsUser3" , Age : 20 , Birthday : parseTime ( "2020-1-1" ) }
2014-08-28 11:33:43 +04:00
DB . Save ( & user1 ) . Save ( & user2 ) . Save ( & user3 )
2014-07-29 08:32:58 +04:00
2014-08-28 11:33:43 +04:00
rows , err := DB . Table ( "users" ) . Where ( "name = ? or name = ?" , user2 . Name , user3 . Name ) . Select ( "name, age" ) . Rows ( )
2014-07-29 08:32:58 +04:00
if err != nil {
2016-02-14 18:29:06 +03:00
t . Errorf ( "Not error should happen, got %v" , err )
2014-07-29 08:32:58 +04:00
}
count := 0
2014-09-19 17:48:55 +04:00
for rows . Next ( ) {
var name string
var age int64
rows . Scan ( & name , & age )
count ++
2014-07-29 08:32:58 +04:00
}
2016-02-14 18:29:06 +03:00
2014-07-29 08:32:58 +04:00
if count != 2 {
2016-02-14 18:29:06 +03:00
t . Errorf ( "Should found two records" )
}
}
func TestScanRows ( t * testing . T ) {
2016-10-19 07:20:45 +03:00
user1 := User { Name : "ScanRowsUser1" , Age : 1 , Birthday : parseTime ( "2000-1-1" ) }
user2 := User { Name : "ScanRowsUser2" , Age : 10 , Birthday : parseTime ( "2010-1-1" ) }
user3 := User { Name : "ScanRowsUser3" , Age : 20 , Birthday : parseTime ( "2020-1-1" ) }
2016-02-14 18:29:06 +03:00
DB . Save ( & user1 ) . Save ( & user2 ) . Save ( & user3 )
rows , err := DB . Table ( "users" ) . Where ( "name = ? or name = ?" , user2 . Name , user3 . Name ) . Select ( "name, age" ) . Rows ( )
if err != nil {
t . Errorf ( "Not error should happen, got %v" , err )
}
type Result struct {
Name string
Age int
}
var results [ ] Result
for rows . Next ( ) {
var result Result
if err := DB . ScanRows ( rows , & result ) ; err != nil {
t . Errorf ( "should get no error, but got %v" , err )
}
results = append ( results , result )
}
if ! reflect . DeepEqual ( results , [ ] Result { { Name : "ScanRowsUser2" , Age : 10 } , { Name : "ScanRowsUser3" , Age : 20 } } ) {
t . Errorf ( "Should find expected results" )
2014-07-29 08:32:58 +04:00
}
}
func TestScan ( t * testing . T ) {
2016-10-19 07:20:45 +03:00
user1 := User { Name : "ScanUser1" , Age : 1 , Birthday : parseTime ( "2000-1-1" ) }
user2 := User { Name : "ScanUser2" , Age : 10 , Birthday : parseTime ( "2010-1-1" ) }
user3 := User { Name : "ScanUser3" , Age : 20 , Birthday : parseTime ( "2020-1-1" ) }
2014-08-28 11:33:43 +04:00
DB . Save ( & user1 ) . Save ( & user2 ) . Save ( & user3 )
2014-07-29 08:32:58 +04:00
type result struct {
Name string
Age int
}
var res result
2014-08-28 11:33:43 +04:00
DB . Table ( "users" ) . Select ( "name, age" ) . Where ( "name = ?" , user3 . Name ) . Scan ( & res )
2014-07-29 08:32:58 +04:00
if res . Name != user3 . Name {
t . Errorf ( "Scan into struct should work" )
}
2017-02-07 03:32:18 +03:00
var doubleAgeRes = & result { }
if err := DB . Table ( "users" ) . Select ( "age + age as age" ) . Where ( "name = ?" , user3 . Name ) . Scan ( & doubleAgeRes ) . Error ; err != nil {
t . Errorf ( "Scan to pointer of pointer" )
}
2014-07-29 08:32:58 +04:00
if doubleAgeRes . Age != res . Age * 2 {
t . Errorf ( "Scan double age as age" )
}
var ress [ ] result
2014-08-28 11:33:43 +04:00
DB . Table ( "users" ) . Select ( "name, age" ) . Where ( "name in (?)" , [ ] string { user2 . Name , user3 . Name } ) . Scan ( & ress )
2014-07-29 08:32:58 +04:00
if len ( ress ) != 2 || ress [ 0 ] . Name != user2 . Name || ress [ 1 ] . Name != user3 . Name {
t . Errorf ( "Scan into struct map" )
}
}
func TestRaw ( t * testing . T ) {
2016-10-19 07:20:45 +03:00
user1 := User { Name : "ExecRawSqlUser1" , Age : 1 , Birthday : parseTime ( "2000-1-1" ) }
user2 := User { Name : "ExecRawSqlUser2" , Age : 10 , Birthday : parseTime ( "2010-1-1" ) }
user3 := User { Name : "ExecRawSqlUser3" , Age : 20 , Birthday : parseTime ( "2020-1-1" ) }
2014-08-28 11:33:43 +04:00
DB . Save ( & user1 ) . Save ( & user2 ) . Save ( & user3 )
2014-07-29 08:32:58 +04:00
type result struct {
Name string
Email string
}
var ress [ ] result
2014-08-28 11:33:43 +04:00
DB . Raw ( "SELECT name, age FROM users WHERE name = ? or name = ?" , user2 . Name , user3 . Name ) . Scan ( & ress )
2014-07-29 08:32:58 +04:00
if len ( ress ) != 2 || ress [ 0 ] . Name != user2 . Name || ress [ 1 ] . Name != user3 . Name {
t . Errorf ( "Raw with scan" )
}
2014-08-28 11:33:43 +04:00
rows , _ := DB . Raw ( "select name, age from users where name = ?" , user3 . Name ) . Rows ( )
2014-07-29 08:32:58 +04:00
count := 0
for rows . Next ( ) {
count ++
}
if count != 1 {
t . Errorf ( "Raw with Rows should find one record with name 3" )
}
2014-08-28 11:33:43 +04:00
DB . Exec ( "update users set name=? where name in (?)" , "jinzhu" , [ ] string { user1 . Name , user2 . Name , user3 . Name } )
2016-03-07 09:54:20 +03:00
if DB . Where ( "name in (?)" , [ ] string { user1 . Name , user2 . Name , user3 . Name } ) . First ( & User { } ) . Error != gorm . ErrRecordNotFound {
2014-07-29 08:32:58 +04:00
t . Error ( "Raw sql to update records" )
}
}
func TestGroup ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
rows , err := DB . Select ( "name" ) . Table ( "users" ) . Group ( "name" ) . Rows ( )
2014-07-29 08:32:58 +04:00
if err == nil {
defer rows . Close ( )
for rows . Next ( ) {
var name string
rows . Scan ( & name )
}
} else {
t . Errorf ( "Should not raise any error" )
}
}
func TestJoins ( t * testing . T ) {
2015-10-01 02:43:38 +03:00
var user = User {
2016-02-21 05:52:30 +03:00
Name : "joins" ,
CreditCard : CreditCard { Number : "411111111111" } ,
Emails : [ ] Email { { Email : "join1@example.com" } , { Email : "join2@example.com" } } ,
2015-10-01 02:43:38 +03:00
}
DB . Save ( & user )
2016-02-16 17:48:26 +03:00
var users1 [ ] User
DB . Joins ( "left join emails on emails.user_id = users.id" ) . Where ( "name = ?" , "joins" ) . Find ( & users1 )
if len ( users1 ) != 2 {
t . Errorf ( "should find two users using left join" )
}
var users2 [ ] User
DB . Joins ( "left join emails on emails.user_id = users.id AND emails.email = ?" , "join1@example.com" ) . Where ( "name = ?" , "joins" ) . First ( & users2 )
if len ( users2 ) != 1 {
t . Errorf ( "should find one users using left join with conditions" )
2015-10-01 02:43:38 +03:00
}
2016-02-21 05:52:30 +03:00
var users3 [ ] User
DB . Joins ( "join emails on emails.user_id = users.id AND emails.email = ?" , "join1@example.com" ) . Joins ( "join credit_cards on credit_cards.user_id = users.id AND credit_cards.number = ?" , "411111111111" ) . Where ( "name = ?" , "joins" ) . First ( & users3 )
if len ( users3 ) != 1 {
t . Errorf ( "should find one users using multiple left join conditions" )
}
var users4 [ ] User
DB . Joins ( "join emails on emails.user_id = users.id AND emails.email = ?" , "join1@example.com" ) . Joins ( "join credit_cards on credit_cards.user_id = users.id AND credit_cards.number = ?" , "422222222222" ) . Where ( "name = ?" , "joins" ) . First ( & users4 )
if len ( users4 ) != 0 {
t . Errorf ( "should find no user when searching with unexisting credit card" )
}
2016-08-09 08:28:43 +03:00
var users5 [ ] User
2016-09-07 16:54:19 +03:00
db5 := DB . Joins ( "join emails on emails.user_id = users.id AND emails.email = ?" , "join1@example.com" ) . Joins ( "join credit_cards on credit_cards.user_id = users.id AND credit_cards.number = ?" , "411111111111" ) . Where ( User { Id : 1 } ) . Where ( Email { Id : 1 } ) . Not ( Email { Id : 10 } ) . First ( & users5 )
2016-08-09 08:28:43 +03:00
if db5 . Error != nil {
t . Errorf ( "Should not raise error for join where identical fields in different tables. Error: %s" , db5 . Error . Error ( ) )
}
2015-10-01 02:43:38 +03:00
}
2018-09-10 01:12:58 +03:00
type JoinedIds struct {
UserID int64 ` gorm:"column:id" `
BillingAddressID int64 ` gorm:"column:id" `
EmailID int64 ` gorm:"column:id" `
}
func TestScanIdenticalColumnNames ( t * testing . T ) {
var user = User {
Name : "joinsIds" ,
Email : "joinIds@example.com" ,
BillingAddress : Address {
Address1 : "One Park Place" ,
} ,
Emails : [ ] Email { { Email : "join1@example.com" } , { Email : "join2@example.com" } } ,
}
DB . Save ( & user )
var users [ ] JoinedIds
DB . Select ( "users.id, addresses.id, emails.id" ) . Table ( "users" ) .
Joins ( "left join addresses on users.billing_address_id = addresses.id" ) .
Joins ( "left join emails on emails.user_id = users.id" ) .
Where ( "name = ?" , "joinsIds" ) . Scan ( & users )
if len ( users ) != 2 {
t . Fatal ( "should find two rows using left join" )
}
if user . Id != users [ 0 ] . UserID {
t . Errorf ( "Expected result row to contain UserID %d, but got %d" , user . Id , users [ 0 ] . UserID )
}
if user . Id != users [ 1 ] . UserID {
t . Errorf ( "Expected result row to contain UserID %d, but got %d" , user . Id , users [ 1 ] . UserID )
}
if user . BillingAddressID . Int64 != users [ 0 ] . BillingAddressID {
t . Errorf ( "Expected result row to contain BillingAddressID %d, but got %d" , user . BillingAddressID . Int64 , users [ 0 ] . BillingAddressID )
}
if user . BillingAddressID . Int64 != users [ 1 ] . BillingAddressID {
t . Errorf ( "Expected result row to contain BillingAddressID %d, but got %d" , user . BillingAddressID . Int64 , users [ 0 ] . BillingAddressID )
}
if users [ 0 ] . EmailID == users [ 1 ] . EmailID {
t . Errorf ( "Email ids should be unique. Got %d and %d" , users [ 0 ] . EmailID , users [ 1 ] . EmailID )
}
if int64 ( user . Emails [ 0 ] . Id ) != users [ 0 ] . EmailID && int64 ( user . Emails [ 1 ] . Id ) != users [ 0 ] . EmailID {
t . Errorf ( "Expected result row ID to be either %d or %d, but was %d" , user . Emails [ 0 ] . Id , user . Emails [ 1 ] . Id , users [ 0 ] . EmailID )
}
if int64 ( user . Emails [ 0 ] . Id ) != users [ 1 ] . EmailID && int64 ( user . Emails [ 1 ] . Id ) != users [ 1 ] . EmailID {
t . Errorf ( "Expected result row ID to be either %d or %d, but was %d" , user . Emails [ 0 ] . Id , user . Emails [ 1 ] . Id , users [ 1 ] . EmailID )
}
}
2015-10-01 02:43:38 +03:00
func TestJoinsWithSelect ( t * testing . T ) {
2014-07-29 08:32:58 +04:00
type result struct {
Name string
Email string
}
user := User {
2015-10-01 02:43:38 +03:00
Name : "joins_with_select" ,
2014-07-29 08:32:58 +04:00
Emails : [ ] Email { { Email : "join1@example.com" } , { Email : "join2@example.com" } } ,
}
2014-08-28 11:33:43 +04:00
DB . Save ( & user )
2014-07-29 08:32:58 +04:00
var results [ ] result
2016-03-10 12:35:19 +03:00
DB . Table ( "users" ) . Select ( "name, emails.email" ) . Joins ( "left join emails on emails.user_id = users.id" ) . Where ( "name = ?" , "joins_with_select" ) . Scan ( & results )
2014-07-29 08:32:58 +04:00
if len ( results ) != 2 || results [ 0 ] . Email != "join1@example.com" || results [ 1 ] . Email != "join2@example.com" {
2015-10-01 02:43:38 +03:00
t . Errorf ( "Should find all two emails with Join select" )
2014-07-29 08:32:58 +04:00
}
}
func TestHaving ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
rows , err := DB . Select ( "name, count(*) as total" ) . Table ( "users" ) . Group ( "name" ) . Having ( "name IN (?)" , [ ] string { "2" , "3" } ) . Rows ( )
2014-07-29 08:32:58 +04:00
if err == nil {
defer rows . Close ( )
for rows . Next ( ) {
var name string
var total int64
rows . Scan ( & name , & total )
if name == "2" && total != 1 {
t . Errorf ( "Should have one user having name 2" )
}
if name == "3" && total != 2 {
t . Errorf ( "Should have two users having name 3" )
}
}
} else {
t . Errorf ( "Should not raise any error" )
}
}
2017-08-02 02:41:43 +03:00
func TestQueryBuilderSubselectInWhere ( t * testing . T ) {
2017-08-11 18:24:00 +03:00
user := User { Name : "query_expr_select_ruser1" , Email : "root@user1.com" , Age : 32 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_select_ruser2" , Email : "nobody@user2.com" , Age : 16 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_select_ruser3" , Email : "root@user3.com" , Age : 64 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_select_ruser4" , Email : "somebody@user3.com" , Age : 128 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
var users [ ] User
DB . Select ( "*" ) . Where ( "name IN (?)" , DB .
2017-08-11 18:24:00 +03:00
Select ( "name" ) . Table ( "users" ) . Where ( "name LIKE ?" , "query_expr_select%" ) . QueryExpr ( ) ) . Find ( & users )
2017-08-02 02:41:43 +03:00
2017-08-11 18:24:00 +03:00
if len ( users ) != 4 {
t . Errorf ( "Four users should be found, instead found %d" , len ( users ) )
2017-08-02 02:41:43 +03:00
}
2017-08-11 18:24:00 +03:00
DB . Select ( "*" ) . Where ( "name LIKE ?" , "query_expr_select%" ) . Where ( "age >= (?)" , DB .
Select ( "AVG(age)" ) . Table ( "users" ) . Where ( "name LIKE ?" , "query_expr_select%" ) . QueryExpr ( ) ) . Find ( & users )
2017-08-02 02:41:43 +03:00
if len ( users ) != 2 {
t . Errorf ( "Two users should be found, instead found %d" , len ( users ) )
}
}
2017-10-23 05:50:44 +03:00
func TestQueryBuilderRawQueryWithSubquery ( t * testing . T ) {
user := User { Name : "subquery_test_user1" , Age : 10 }
DB . Save ( & user )
user = User { Name : "subquery_test_user2" , Age : 11 }
DB . Save ( & user )
2018-02-11 10:52:52 +03:00
user = User { Name : "subquery_test_user3" , Age : 12 }
2017-10-23 05:50:44 +03:00
DB . Save ( & user )
var count int
err := DB . Raw ( "select count(*) from (?) tmp" ,
DB . Table ( "users" ) .
Select ( "name" ) .
Where ( "age >= ? and name in (?)" , 10 , [ ] string { "subquery_test_user1" , "subquery_test_user2" } ) .
Group ( "name" ) .
QueryExpr ( ) ,
) . Count ( & count ) . Error
2018-02-11 10:52:52 +03:00
2017-10-23 05:50:44 +03:00
if err != nil {
t . Errorf ( "Expected to get no errors, but got %v" , err )
}
if count != 2 {
t . Errorf ( "Row count must be 2, instead got %d" , count )
}
2018-02-11 10:52:52 +03:00
err = DB . Raw ( "select count(*) from (?) tmp" ,
DB . Table ( "users" ) .
Select ( "name" ) .
Where ( "name LIKE ?" , "subquery_test%" ) .
Not ( "age <= ?" , 10 ) . Not ( "name in (?)" , [ ] string { "subquery_test_user1" , "subquery_test_user2" } ) .
Group ( "name" ) .
QueryExpr ( ) ,
) . Count ( & count ) . Error
if err != nil {
t . Errorf ( "Expected to get no errors, but got %v" , err )
}
if count != 1 {
t . Errorf ( "Row count must be 1, instead got %d" , count )
}
2017-10-23 05:50:44 +03:00
}
2017-08-02 02:41:43 +03:00
func TestQueryBuilderSubselectInHaving ( t * testing . T ) {
2017-08-11 18:24:00 +03:00
user := User { Name : "query_expr_having_ruser1" , Email : "root@user1.com" , Age : 64 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_having_ruser2" , Email : "root@user2.com" , Age : 128 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_having_ruser3" , Email : "root@user1.com" , Age : 64 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
2017-08-11 18:24:00 +03:00
user = User { Name : "query_expr_having_ruser4" , Email : "root@user2.com" , Age : 128 }
2017-08-02 02:41:43 +03:00
DB . Save ( & user )
var users [ ] User
2017-08-11 18:24:00 +03:00
DB . Select ( "AVG(age) as avgage" ) . Where ( "name LIKE ?" , "query_expr_having_%" ) . Group ( "email" ) . Having ( "AVG(age) > (?)" , DB .
Select ( "AVG(age)" ) . Where ( "name LIKE ?" , "query_expr_having_%" ) . Table ( "users" ) . QueryExpr ( ) ) . Find ( & users )
2017-08-02 02:41:43 +03:00
if len ( users ) != 1 {
2017-08-11 18:24:00 +03:00
t . Errorf ( "Two user group should be found, instead found %d" , len ( users ) )
2017-08-02 02:41:43 +03:00
}
}
2015-03-24 20:33:51 +03:00
func DialectHasTzSupport ( ) bool {
// NB: mssql and FoundationDB do not support time zones.
2017-08-02 02:41:43 +03:00
if dialect := os . Getenv ( "GORM_DIALECT" ) ; dialect == "foundation" {
2015-03-24 20:33:51 +03:00
return false
}
return true
}
2014-07-29 08:32:58 +04:00
func TestTimeWithZone ( t * testing . T ) {
var format = "2006-01-02 15:04:05 -0700"
var times [ ] time . Time
GMT8 , _ := time . LoadLocation ( "Asia/Shanghai" )
times = append ( times , time . Date ( 2013 , 02 , 19 , 1 , 51 , 49 , 123456789 , GMT8 ) )
times = append ( times , time . Date ( 2013 , 02 , 18 , 17 , 51 , 49 , 123456789 , time . UTC ) )
for index , vtime := range times {
name := "time_with_zone_" + strconv . Itoa ( index )
2016-10-19 07:20:45 +03:00
user := User { Name : name , Birthday : & vtime }
2014-09-16 00:03:14 +04:00
2015-03-24 20:33:51 +03:00
if ! DialectHasTzSupport ( ) {
// If our driver dialect doesn't support TZ's, just use UTC for everything here.
2016-10-19 07:20:45 +03:00
utcBirthday := user . Birthday . UTC ( )
user . Birthday = & utcBirthday
2014-09-16 00:03:14 +04:00
}
2015-03-24 20:33:51 +03:00
2014-08-28 11:33:43 +04:00
DB . Save ( & user )
2015-03-24 20:33:51 +03:00
expectedBirthday := "2013-02-18 17:51:49 +0000"
foundBirthday := user . Birthday . UTC ( ) . Format ( format )
if foundBirthday != expectedBirthday {
t . Errorf ( "User's birthday should not be changed after save for name=%s, expected bday=%+v but actual value=%+v" , name , expectedBirthday , foundBirthday )
2014-07-29 08:32:58 +04:00
}
var findUser , findUser2 , findUser3 User
2014-08-28 11:33:43 +04:00
DB . First ( & findUser , "name = ?" , name )
2015-03-24 20:33:51 +03:00
foundBirthday = findUser . Birthday . UTC ( ) . Format ( format )
if foundBirthday != expectedBirthday {
2016-01-15 16:03:35 +03:00
t . Errorf ( "User's birthday should not be changed after find for name=%s, expected bday=%+v but actual value=%+v" , name , expectedBirthday , foundBirthday )
2014-07-29 08:32:58 +04:00
}
2015-03-24 20:33:51 +03:00
if DB . Where ( "id = ? AND birthday >= ?" , findUser . Id , user . Birthday . Add ( - time . Minute ) ) . First ( & findUser2 ) . RecordNotFound ( ) {
2014-07-29 08:32:58 +04:00
t . Errorf ( "User should be found" )
}
2015-03-24 20:33:51 +03:00
if ! DB . Where ( "id = ? AND birthday >= ?" , findUser . Id , user . Birthday . Add ( time . Minute ) ) . First ( & findUser3 ) . RecordNotFound ( ) {
2014-07-29 08:32:58 +04:00
t . Errorf ( "User should not be found" )
}
}
}
func TestHstore ( t * testing . T ) {
2014-07-29 12:25:38 +04:00
type Details struct {
Id int64
2016-03-05 13:54:59 +03:00
Bulk postgres . Hstore
2014-07-29 12:25:38 +04:00
}
2014-07-29 08:32:58 +04:00
if dialect := os . Getenv ( "GORM_DIALECT" ) ; dialect != "postgres" {
t . Skip ( )
}
2014-08-28 11:33:43 +04:00
if err := DB . Exec ( "CREATE EXTENSION IF NOT EXISTS hstore" ) . Error ; err != nil {
2014-07-29 08:32:58 +04:00
fmt . Println ( "\033[31mHINT: Must be superuser to create hstore extension (ALTER USER gorm WITH SUPERUSER;)\033[0m" )
panic ( fmt . Sprintf ( "No error should happen when create hstore extension, but got %+v" , err ) )
}
2014-08-28 11:33:43 +04:00
DB . Exec ( "drop table details" )
2014-07-29 08:32:58 +04:00
2014-08-28 11:33:43 +04:00
if err := DB . CreateTable ( & Details { } ) . Error ; err != nil {
2014-07-29 08:32:58 +04:00
panic ( fmt . Sprintf ( "No error should happen when create table, but got %+v" , err ) )
}
bankAccountId , phoneNumber , opinion := "123456" , "14151321232" , "sharkbait"
bulk := map [ string ] * string {
"bankAccountId" : & bankAccountId ,
"phoneNumber" : & phoneNumber ,
"opinion" : & opinion ,
}
d := Details { Bulk : bulk }
2014-08-28 11:33:43 +04:00
DB . Save ( & d )
2014-07-29 08:32:58 +04:00
var d2 Details
2014-08-28 11:33:43 +04:00
if err := DB . First ( & d2 ) . Error ; err != nil {
2014-07-29 08:32:58 +04:00
t . Errorf ( "Got error when tried to fetch details: %+v" , err )
}
for k := range bulk {
if r , ok := d2 . Bulk [ k ] ; ok {
if res , _ := bulk [ k ] ; * res != * r {
t . Errorf ( "Details should be equal" )
}
} else {
t . Errorf ( "Details should be existed" )
}
}
}
2014-08-20 13:05:02 +04:00
func TestSetAndGet ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
if value , ok := DB . Set ( "hello" , "world" ) . Get ( "hello" ) ; ! ok {
2014-08-20 13:05:02 +04:00
t . Errorf ( "Should be able to get setting after set" )
} else {
if value . ( string ) != "world" {
t . Errorf ( "Setted value should not be changed" )
}
}
2014-08-28 11:33:43 +04:00
if _ , ok := DB . Get ( "non_existing" ) ; ok {
2014-08-20 13:05:02 +04:00
t . Errorf ( "Get non existing key should return error" )
}
}
2014-07-29 08:32:58 +04:00
func TestCompatibilityMode ( t * testing . T ) {
2014-08-28 11:33:43 +04:00
DB , _ := gorm . Open ( "testdb" , "" )
2014-07-29 08:32:58 +04:00
testdb . SetQueryFunc ( func ( query string ) ( driver . Rows , error ) {
columns := [ ] string { "id" , "name" , "age" }
result := `
1 , Tim , 20
2 , Joe , 25
3 , Bob , 30
`
return testdb . RowsFromCSVString ( columns , result ) , nil
} )
var users [ ] User
2014-08-28 11:33:43 +04:00
DB . Find ( & users )
2014-07-29 08:32:58 +04:00
if ( users [ 0 ] . Name != "Tim" ) || len ( users ) != 3 {
t . Errorf ( "Unexcepted result returned" )
}
}
2014-11-22 22:24:26 +03:00
func TestOpenExistingDB ( t * testing . T ) {
DB . Save ( & User { Name : "jnfeinstein" } )
dialect := os . Getenv ( "GORM_DIALECT" )
db , err := gorm . Open ( dialect , DB . DB ( ) )
if err != nil {
t . Errorf ( "Should have wrapped the existing DB connection" )
}
var user User
2016-03-07 09:54:20 +03:00
if db . Where ( "name = ?" , "jnfeinstein" ) . First ( & user ) . Error == gorm . ErrRecordNotFound {
2014-11-22 22:24:26 +03:00
t . Errorf ( "Should have found existing record" )
}
}
2016-03-08 16:45:20 +03:00
func TestDdlErrors ( t * testing . T ) {
var err error
if err = DB . Close ( ) ; err != nil {
t . Errorf ( "Closing DDL test db connection err=%s" , err )
}
defer func ( ) {
// Reopen DB connection.
if DB , err = OpenTestConnection ( ) ; err != nil {
t . Fatalf ( "Failed re-opening db connection: %s" , err )
}
} ( )
if err := DB . Find ( & User { } ) . Error ; err == nil {
t . Errorf ( "Expected operation on closed db to produce an error, but err was nil" )
}
}
2016-10-08 19:22:15 +03:00
func TestOpenWithOneParameter ( t * testing . T ) {
db , err := gorm . Open ( "dialect" )
if db != nil {
t . Error ( "Open with one parameter returned non nil for db" )
}
if err == nil {
t . Error ( "Open with one parameter returned err as nil" )
}
}
2018-09-10 01:25:26 +03:00
func TestSaveAssociations ( t * testing . T ) {
db := DB . New ( )
deltaAddressCount := 0
if err := db . Model ( & Address { } ) . Count ( & deltaAddressCount ) . Error ; err != nil {
t . Errorf ( "failed to fetch address count" )
t . FailNow ( )
}
placeAddress := & Address {
Address1 : "somewhere on earth" ,
}
ownerAddress1 := & Address {
Address1 : "near place address" ,
}
ownerAddress2 := & Address {
Address1 : "address2" ,
}
db . Create ( placeAddress )
addressCountShouldBe := func ( t * testing . T , expectedCount int ) {
countFromDB := 0
t . Helper ( )
err := db . Model ( & Address { } ) . Count ( & countFromDB ) . Error
if err != nil {
t . Error ( "failed to fetch address count" )
}
if countFromDB != expectedCount {
t . Errorf ( "address count mismatch: %d" , countFromDB )
}
}
addressCountShouldBe ( t , deltaAddressCount + 1 )
// owner address should be created, place address should be reused
place1 := & Place {
PlaceAddressID : placeAddress . ID ,
PlaceAddress : placeAddress ,
OwnerAddress : ownerAddress1 ,
}
err := db . Create ( place1 ) . Error
if err != nil {
t . Errorf ( "failed to store place: %s" , err . Error ( ) )
}
addressCountShouldBe ( t , deltaAddressCount + 2 )
// owner address should be created again, place address should be reused
place2 := & Place {
PlaceAddressID : placeAddress . ID ,
PlaceAddress : & Address {
ID : 777 ,
Address1 : "address1" ,
} ,
OwnerAddress : ownerAddress2 ,
OwnerAddressID : 778 ,
}
err = db . Create ( place2 ) . Error
if err != nil {
t . Errorf ( "failed to store place: %s" , err . Error ( ) )
}
addressCountShouldBe ( t , deltaAddressCount + 3 )
count := 0
db . Model ( & Place { } ) . Where ( & Place {
PlaceAddressID : placeAddress . ID ,
OwnerAddressID : ownerAddress1 . ID ,
} ) . Count ( & count )
if count != 1 {
t . Errorf ( "only one instance of (%d, %d) should be available, found: %d" ,
placeAddress . ID , ownerAddress1 . ID , count )
}
db . Model ( & Place { } ) . Where ( & Place {
PlaceAddressID : placeAddress . ID ,
OwnerAddressID : ownerAddress2 . ID ,
} ) . Count ( & count )
if count != 1 {
t . Errorf ( "only one instance of (%d, %d) should be available, found: %d" ,
placeAddress . ID , ownerAddress2 . ID , count )
}
db . Model ( & Place { } ) . Where ( & Place {
PlaceAddressID : placeAddress . ID ,
} ) . Count ( & count )
if count != 2 {
t . Errorf ( "two instances of (%d) should be available, found: %d" ,
placeAddress . ID , count )
}
}
2016-11-04 19:41:31 +03:00
func TestBlockGlobalUpdate ( t * testing . T ) {
db := DB . New ( )
db . Create ( & Toy { Name : "Stuffed Animal" , OwnerType : "Nobody" } )
err := db . Model ( & Toy { } ) . Update ( "OwnerType" , "Human" ) . Error
if err != nil {
t . Error ( "Unexpected error on global update" )
}
err = db . Delete ( & Toy { } ) . Error
if err != nil {
t . Error ( "Unexpected error on global delete" )
}
db . BlockGlobalUpdate ( true )
db . Create ( & Toy { Name : "Stuffed Animal" , OwnerType : "Nobody" } )
err = db . Model ( & Toy { } ) . Update ( "OwnerType" , "Human" ) . Error
if err == nil {
t . Error ( "Expected error on global update" )
}
err = db . Model ( & Toy { } ) . Where ( & Toy { OwnerType : "Martian" } ) . Update ( "OwnerType" , "Astronaut" ) . Error
if err != nil {
t . Error ( "Unxpected error on conditional update" )
}
err = db . Delete ( & Toy { } ) . Error
if err == nil {
t . Error ( "Expected error on global delete" )
}
err = db . Where ( & Toy { OwnerType : "Martian" } ) . Delete ( & Toy { } ) . Error
if err != nil {
t . Error ( "Unexpected error on conditional delete" )
}
}
2019-03-11 14:56:03 +03:00
func TestCountWithHaving ( t * testing . T ) {
db := DB . New ( )
db . Delete ( User { } )
defer db . Delete ( User { } )
DB . Create ( getPreparedUser ( "user1" , "pluck_user" ) )
DB . Create ( getPreparedUser ( "user2" , "pluck_user" ) )
2019-04-14 10:38:06 +03:00
user3 := getPreparedUser ( "user3" , "pluck_user" )
user3 . Languages = [ ] Language { }
2019-03-11 14:56:03 +03:00
DB . Create ( user3 )
var count int
2019-04-14 10:38:06 +03:00
err := db . Model ( User { } ) . Select ( "users.id" ) .
2019-03-11 14:56:03 +03:00
Joins ( "LEFT JOIN user_languages ON user_languages.user_id = users.id" ) .
Joins ( "LEFT JOIN languages ON user_languages.language_id = languages.id" ) .
Group ( "users.id" ) . Having ( "COUNT(languages.id) > 1" ) . Count ( & count ) . Error
if err != nil {
t . Error ( "Unexpected error on query count with having" )
}
2019-04-14 10:38:06 +03:00
if count != 2 {
2019-03-11 14:56:03 +03:00
t . Error ( "Unexpected result on query count with having" )
}
}
2019-04-19 14:41:30 +03:00
func TestPluck ( t * testing . T ) {
db := DB . New ( )
db . Delete ( User { } )
defer db . Delete ( User { } )
DB . Create ( & User { Id : 1 , Name : "user1" } )
DB . Create ( & User { Id : 2 , Name : "user2" } )
DB . Create ( & User { Id : 3 , Name : "user3" } )
var ids [ ] int64
err := db . Model ( User { } ) . Order ( "id" ) . Pluck ( "id" , & ids ) . Error
if err != nil {
t . Error ( "Unexpected error on pluck" )
}
if len ( ids ) != 3 || ids [ 0 ] != 1 || ids [ 1 ] != 2 || ids [ 2 ] != 3 {
t . Error ( "Unexpected result on pluck" )
}
err = db . Model ( User { } ) . Order ( "id" ) . Pluck ( "id" , & ids ) . Error
if err != nil {
t . Error ( "Unexpected error on pluck again" )
}
if len ( ids ) != 3 || ids [ 0 ] != 1 || ids [ 1 ] != 2 || ids [ 2 ] != 3 {
t . Error ( "Unexpected result on pluck again" )
}
}
2019-04-19 14:48:52 +03:00
func TestCountWithQueryOption ( t * testing . T ) {
db := DB . New ( )
db . Delete ( User { } )
defer db . Delete ( User { } )
DB . Create ( & User { Name : "user1" } )
DB . Create ( & User { Name : "user2" } )
DB . Create ( & User { Name : "user3" } )
var count int
err := db . Model ( User { } ) . Select ( "users.id" ) .
Set ( "gorm:query_option" , "WHERE users.name='user2'" ) .
Count ( & count ) . Error
if err != nil {
t . Error ( "Unexpected error on query count with query_option" )
}
if count != 1 {
t . Error ( "Unexpected result on query count with query_option" )
}
}
2019-05-05 10:24:26 +03:00
func TestFloatColumnPrecision ( t * testing . T ) {
2019-05-05 10:51:05 +03:00
if dialect := os . Getenv ( "GORM_DIALECT" ) ; dialect != "mysql" && dialect != "sqlite" {
t . Skip ( )
}
2019-05-05 10:24:26 +03:00
type FloatTest struct {
ID string ` gorm:"primary_key" `
FloatValue float64 ` gorm:"column:float_value" sql:"type:float(255,5);" `
}
DB . DropTable ( & FloatTest { } )
DB . AutoMigrate ( & FloatTest { } )
data := FloatTest { ID : "uuid" , FloatValue : 112.57315 }
if err := DB . Save ( & data ) . Error ; err != nil || data . ID != "uuid" || data . FloatValue != 112.57315 {
t . Errorf ( "Float value should not lose precision" )
}
}
2019-05-05 12:12:03 +03:00
func TestWhereUpdates ( t * testing . T ) {
type OwnerEntity struct {
gorm . Model
OwnerID uint
OwnerType string
}
type SomeEntity struct {
gorm . Model
Name string
OwnerEntity OwnerEntity ` gorm:"polymorphic:Owner" `
}
2019-06-13 07:20:11 +03:00
DB . DropTable ( & SomeEntity { } )
DB . AutoMigrate ( & SomeEntity { } )
2019-05-05 12:12:03 +03:00
a := SomeEntity { Name : "test" }
2019-06-13 07:20:11 +03:00
DB . Model ( & a ) . Where ( a ) . Updates ( SomeEntity { Name : "test2" } )
2019-05-05 12:12:03 +03:00
}
2014-07-29 08:32:58 +04:00
func BenchmarkGorm ( b * testing . B ) {
b . N = 2000
for x := 0 ; x < b . N ; x ++ {
e := strconv . Itoa ( x ) + "benchmark@example.org"
2016-10-19 07:20:45 +03:00
now := time . Now ( )
2017-03-22 17:57:13 +03:00
email := EmailWithIdx { Email : e , UserAgent : "pc" , RegisteredAt : & now }
2014-07-29 08:32:58 +04:00
// Insert
2014-08-28 11:33:43 +04:00
DB . Save ( & email )
2014-07-29 08:32:58 +04:00
// Query
2017-03-22 17:57:13 +03:00
DB . First ( & EmailWithIdx { } , "email = ?" , e )
2014-07-29 08:32:58 +04:00
// Update
2014-08-28 11:33:43 +04:00
DB . Model ( & email ) . UpdateColumn ( "email" , "new-" + e )
2014-07-29 08:32:58 +04:00
// Delete
2014-08-28 11:33:43 +04:00
DB . Delete ( & email )
2014-07-29 08:32:58 +04:00
}
}
func BenchmarkRawSql ( b * testing . B ) {
2014-08-28 11:33:43 +04:00
DB , _ := sql . Open ( "postgres" , "user=gorm DB.ame=gorm sslmode=disable" )
DB . SetMaxIdleConns ( 10 )
2015-02-17 03:34:01 +03:00
insertSql := "INSERT INTO emails (user_id,email,user_agent,registered_at,created_at,updated_at) VALUES ($1,$2,$3,$4,$5,$6) RETURNING id"
querySql := "SELECT * FROM emails WHERE email = $1 ORDER BY id LIMIT 1"
updateSql := "UPDATE emails SET email = $1, updated_at = $2 WHERE id = $3"
deleteSql := "DELETE FROM orders WHERE id = $1"
2014-07-29 08:32:58 +04:00
b . N = 2000
for x := 0 ; x < b . N ; x ++ {
var id int64
e := strconv . Itoa ( x ) + "benchmark@example.org"
2016-10-19 07:20:45 +03:00
now := time . Now ( )
2017-03-22 17:57:13 +03:00
email := EmailWithIdx { Email : e , UserAgent : "pc" , RegisteredAt : & now }
2014-07-29 08:32:58 +04:00
// Insert
2015-02-17 03:34:01 +03:00
DB . QueryRow ( insertSql , email . UserId , email . Email , email . UserAgent , email . RegisteredAt , time . Now ( ) , time . Now ( ) ) . Scan ( & id )
2014-07-29 08:32:58 +04:00
// Query
2015-02-17 03:34:01 +03:00
rows , _ := DB . Query ( querySql , email . Email )
2014-07-29 08:32:58 +04:00
rows . Close ( )
// Update
2015-02-17 03:34:01 +03:00
DB . Exec ( updateSql , "new-" + e , time . Now ( ) , id )
2014-07-29 08:32:58 +04:00
// Delete
2015-02-17 03:34:01 +03:00
DB . Exec ( deleteSql , id )
2014-07-29 08:32:58 +04:00
}
}
2016-10-19 07:20:45 +03:00
func parseTime ( str string ) * time . Time {
2017-03-22 17:57:13 +03:00
t := now . New ( time . Now ( ) . UTC ( ) ) . MustParse ( str )
2016-10-19 07:20:45 +03:00
return & t
}