github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/sqlx/database_test.go (about)

     1  package sqlx_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"fmt"
     7  	"os"
     8  	"testing"
     9  	"time"
    10  
    11  	_ "github.com/go-sql-driver/mysql"
    12  	"github.com/google/uuid"
    13  	"github.com/sirupsen/logrus"
    14  	"github.com/stretchr/testify/assert"
    15  
    16  	"github.com/johnnyeven/libtools/runner"
    17  	"github.com/johnnyeven/libtools/sqlx"
    18  	"github.com/johnnyeven/libtools/sqlx/builder"
    19  	"github.com/johnnyeven/libtools/timelib"
    20  )
    21  
    22  var db *sqlx.DB
    23  
    24  func init() {
    25  	logrus.SetLevel(logrus.DebugLevel)
    26  	db, _ = sqlx.Open("logger:mysql", "root:root@tcp(localhost:3306)/?charset=utf8&parseTime=true&interpolateParams=true&autocommit=true&loc=Local")
    27  }
    28  
    29  type TableOperateTime struct {
    30  	CreatedAt timelib.MySQLDatetime `db:"F_created_at" sql:"datetime(6) NOT NULL DEFAULT '0' ON UPDATE CURRENT_TIMESTAMP(6)" `
    31  	UpdatedAt int64                 `db:"F_updated_at" sql:"bigint(64) NOT NULL DEFAULT '0'"`
    32  }
    33  
    34  func (t *TableOperateTime) BeforeUpdate() {
    35  	time.Now()
    36  	t.UpdatedAt = time.Now().UnixNano()
    37  }
    38  
    39  func (t *TableOperateTime) BeforeInsert() {
    40  	t.CreatedAt = timelib.MySQLDatetime(time.Now())
    41  	t.UpdatedAt = t.CreatedAt.Unix()
    42  }
    43  
    44  type Gender int
    45  
    46  const (
    47  	GenderMale Gender = iota + 1
    48  	GenderFemale
    49  )
    50  
    51  func (Gender) EnumType() string {
    52  	return "Gender"
    53  }
    54  
    55  func (Gender) Enums() map[int][]string {
    56  	return map[int][]string{
    57  		int(GenderMale):   {"male", "男"},
    58  		int(GenderFemale): {"female", "女"},
    59  	}
    60  }
    61  
    62  func (g Gender) String() string {
    63  	switch g {
    64  	case GenderMale:
    65  		return "male"
    66  	case GenderFemale:
    67  		return "female"
    68  	}
    69  	return ""
    70  }
    71  
    72  // @def primary ID
    73  // @def index I_nickname Nickname Name
    74  // @def unique_index I_name Name
    75  type User struct {
    76  	ID       uint64 `db:"F_id" sql:"bigint(64) unsigned NOT NULL AUTO_INCREMENT"`
    77  	Name     string `db:"F_name" sql:"varchar(255) binary NOT NULL DEFAULT ''"`
    78  	Username string `db:"F_username" sql:"varchar(255)"`
    79  	Nickname string `db:"F_nickname" sql:"varchar(255) CHARACTER SET latin1 binary NOT NULL DEFAULT ''"`
    80  	Gender   Gender `db:"F_gender" sql:"int(32) NOT NULL DEFAULT '0'"`
    81  
    82  	TableOperateTime
    83  }
    84  
    85  func (user *User) Comments() map[string]string {
    86  	return map[string]string{
    87  		"Name": "姓名",
    88  	}
    89  }
    90  
    91  func (user *User) AfterInsert(result sql.Result) error {
    92  	lastInsertID, err := result.LastInsertId()
    93  	if err != nil {
    94  		return err
    95  	}
    96  	user.ID = uint64(lastInsertID)
    97  	return nil
    98  }
    99  
   100  func (user *User) TableName() string {
   101  	return "t_user"
   102  }
   103  
   104  func (user *User) PrimaryKey() sqlx.FieldNames {
   105  	return []string{"ID"}
   106  }
   107  
   108  func (user *User) Indexes() sqlx.Indexes {
   109  	return sqlx.Indexes{
   110  		"I_nickname": {"Nickname", "Name"},
   111  	}
   112  }
   113  
   114  func (user *User) UniqueIndexes() sqlx.Indexes {
   115  	return sqlx.Indexes{
   116  		"I_name": {"Name"},
   117  	}
   118  }
   119  
   120  type User2 struct {
   121  	User
   122  	Age int32 `db:"F_age" sql:"int(32) NOT NULL DEFAULT '0'"`
   123  }
   124  
   125  func (user2 *User2) Indexes() sqlx.Indexes {
   126  	return sqlx.Indexes{}
   127  }
   128  
   129  func TestMigrate(t *testing.T) {
   130  	tt := assert.New(t)
   131  
   132  	os.Setenv("PROJECT_FEATURE", "test")
   133  	dbTest := sqlx.NewFeatureDatabase("test_for_migrate")
   134  	defer func() {
   135  		err := db.Do(dbTest.Drop()).Err()
   136  		tt.Nil(err)
   137  	}()
   138  
   139  	{
   140  		dbTest.Register(&User{})
   141  		err := dbTest.MigrateTo(db, false)
   142  		tt.NoError(err)
   143  	}
   144  	{
   145  		dbTest.Register(&User{})
   146  		err := dbTest.MigrateTo(db, false)
   147  		tt.NoError(err)
   148  	}
   149  	{
   150  		dbTest.Register(&User2{})
   151  		err := dbTest.MigrateTo(db, false)
   152  		tt.NoError(err)
   153  	}
   154  
   155  	{
   156  		dbTest.Register(&User{})
   157  		err := dbTest.MigrateTo(db, false)
   158  		tt.NoError(err)
   159  	}
   160  }
   161  
   162  func TestCRUD(t *testing.T) {
   163  	tt := assert.New(t)
   164  
   165  	dbTest := sqlx.NewDatabase("test")
   166  	defer func() {
   167  		err := db.Do(dbTest.Drop()).Err()
   168  		tt.Nil(err)
   169  	}()
   170  
   171  	userTable := dbTest.Register(&User{})
   172  	err := dbTest.MigrateTo(db, false)
   173  	tt.Nil(err)
   174  
   175  	{
   176  		user := User{
   177  			Name:   uuid.New().String(),
   178  			Gender: GenderMale,
   179  		}
   180  		user.BeforeInsert()
   181  		dbRet := db.Do(dbTest.Insert(&user).Comment("InsertUser"))
   182  		err := dbRet.Err()
   183  		tt.Nil(err)
   184  		user.AfterInsert(dbRet.Result)
   185  		tt.NotEmpty(user.ID)
   186  
   187  		{
   188  			user.Gender = GenderFemale
   189  			user.BeforeUpdate()
   190  			err := db.Do(
   191  				dbTest.Update(&user).
   192  					Where(
   193  						userTable.F("Name").Eq(user.Name),
   194  					).
   195  					Comment("UpdateUser"),
   196  			).
   197  				Err()
   198  			tt.Nil(err)
   199  		}
   200  
   201  		{
   202  			userForSelect := User{}
   203  			err := db.Do(
   204  				userTable.Select().Where(userTable.F("Name").Eq(user.Name)).Comment("FindUser"),
   205  			).
   206  				Scan(&userForSelect).
   207  				Err()
   208  			tt.Nil(err)
   209  			tt.Equal(userForSelect.Name, user.Name)
   210  			tt.Equal(userForSelect.CreatedAt.Unix(), user.CreatedAt.Unix())
   211  		}
   212  
   213  		{
   214  			user.BeforeInsert()
   215  			err := db.Do(dbTest.Insert(&user).Comment("Insert Conflict")).Err()
   216  			t.Log(err)
   217  			tt.True(sqlx.DBErr(err).IsConflict())
   218  
   219  			{
   220  				err := db.Do(
   221  					dbTest.Insert(&user).
   222  						OnDuplicateKeyUpdate(
   223  							userTable.AssignsByFieldValues(builder.FieldValues{
   224  								"Gender": GenderMale,
   225  							})...,
   226  						).Comment("InsertUserOnDuplicate"),
   227  				).Err()
   228  				tt.Nil(err)
   229  			}
   230  		}
   231  	}
   232  
   233  }
   234  
   235  func TestSelect(t *testing.T) {
   236  	tt := assert.New(t)
   237  
   238  	dbTest := sqlx.NewDatabase("test2")
   239  	defer func() {
   240  		err := db.Do(dbTest.Drop()).Err()
   241  		tt.Nil(err)
   242  	}()
   243  
   244  	table := dbTest.Register(&User{})
   245  	err := dbTest.MigrateTo(db, false)
   246  	tt.Nil(err)
   247  
   248  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
   249  	defer cancel()
   250  
   251  	r := runner.NewRunner(ctx, "create data", 30)
   252  
   253  	for i := 0; i < 10; i++ {
   254  		r.Add(func(ctx context.Context) error {
   255  			user := User{
   256  				Name:   uuid.New().String(),
   257  				Gender: GenderMale,
   258  			}
   259  			user.BeforeInsert()
   260  			return db.Do(dbTest.Insert(&user).Comment("InsertUser")).Scan(&user).Err()
   261  		}, fmt.Sprintf("%d", i))
   262  	}
   263  
   264  	tt.NoError(r.Commit())
   265  
   266  	{
   267  		users := make([]User, 0)
   268  		err := db.Do(table.Select().Where(table.F("Gender").Eq(GenderMale))).Scan(&users).Err()
   269  		tt.NoError(err)
   270  		tt.Len(users, 10)
   271  	}
   272  	{
   273  		user := User{}
   274  		err := db.Do(table.Select().Where(table.F("ID").Eq(11))).Scan(&user).Err()
   275  		tt.True(sqlx.DBErr(err).IsNotFound())
   276  	}
   277  	{
   278  		count := 0
   279  		err := db.Do(
   280  			table.Select().For(builder.Count(builder.Star())),
   281  		).Scan(&count).Err()
   282  		tt.NoError(err)
   283  		tt.Equal(10, count)
   284  	}
   285  	{
   286  		user := &User{}
   287  		err := db.Do(table.Select().Where(table.F("Gender").Eq(GenderMale))).Scan(user).Err()
   288  		tt.Error(err)
   289  	}
   290  }