2014-01-28 12:21:02 +04:00
package gorm_test
2013-10-25 18:31:56 +04:00
2013-10-26 20:36:56 +04:00
import (
2014-07-29 08:32:58 +04:00
"database/sql"
"database/sql/driver"
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"
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 ) {
2013-12-04 08:06:50 +04:00
switch os . Getenv ( "GORM_DIALECT" ) {
2014-09-16 19:32:35 +04:00
case "mysql" :
// CREATE USER 'gorm'@'localhost' IDENTIFIED BY 'gorm';
// CREATE DATABASE gorm;
// GRANT ALL ON gorm.* TO 'gorm'@'localhost';
fmt . Println ( "testing mysql..." )
2016-05-21 21:05:31 +03:00
dbhost := os . Getenv ( "GORM_DBADDRESS" )
if dbhost != "" {
dbhost = fmt . Sprintf ( "tcp(%v)" , dbhost )
}
db , err = gorm . Open ( "mysql" , fmt . Sprintf ( "gorm:gorm@%v/gorm?charset=utf8&parseTime=True" , dbhost ) )
2014-09-16 19:32:35 +04:00
case "postgres" :
fmt . Println ( "testing postgres..." )
2016-05-21 21:05:31 +03:00
dbhost := os . Getenv ( "GORM_DBHOST" )
if dbhost != "" {
dbhost = fmt . Sprintf ( "host=%v " , dbhost )
}
db , err = gorm . Open ( "postgres" , fmt . Sprintf ( "%vuser=gorm password=gorm DB.name=gorm sslmode=disable" , dbhost ) )
2015-03-24 20:33:51 +03:00
case "foundation" :
fmt . Println ( "testing foundation..." )
2015-08-09 00:08:25 +03:00
db , err = gorm . Open ( "foundation" , "dbname=gorm port=15432 sslmode=disable" )
2014-09-16 19:32:35 +04:00
case "mssql" :
fmt . Println ( "testing mssql..." )
2015-08-09 00:08:25 +03:00
db , err = gorm . Open ( "mssql" , "server=SERVER_HERE;database=rogue;user id=USER_HERE;password=PW_HERE;port=1433" )
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))
if os . Getenv ( "DEBUG" ) == "true" {
db . LogMode ( true )
}
db . DB ( ) . SetMaxIdleConns ( 10 )
2015-08-09 00:08:25 +03:00
return
2014-07-29 06:59:13 +04:00
}
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" )
}
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
}
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" )
}
}
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" )
}
var doubleAgeRes result
2014-08-28 11:33:43 +04:00
DB . Table ( "users" ) . Select ( "age + age as age" ) . Where ( "name = ?" , user3 . Name ) . Scan ( & doubleAgeRes )
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
}
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" )
}
}
2015-03-24 20:33:51 +03:00
func DialectHasTzSupport ( ) bool {
// NB: mssql and FoundationDB do not support time zones.
if dialect := os . Getenv ( "GORM_DIALECT" ) ; dialect == "mssql" || dialect == "foundation" {
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" )
}
}
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" )
}
}
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 ( )
email := BigEmail { 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
2014-08-28 11:33:43 +04:00
DB . First ( & BigEmail { } , "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 ( )
email := BigEmail { 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 {
t := now . MustParse ( str )
return & t
}