github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/xlog/gorm_test.go (about) 1 package xlog 2 3 import ( 4 "context" 5 "database/sql" 6 "database/sql/driver" 7 "errors" 8 "testing" 9 "time" 10 11 mock "github.com/DATA-DOG/go-sqlmock" 12 "github.com/glebarez/sqlite" 13 "github.com/stretchr/testify/require" 14 "go.uber.org/zap" 15 "go.uber.org/zap/zapcore" 16 "gorm.io/gorm" 17 glogger "gorm.io/gorm/logger" 18 ) 19 20 func genDBMock(logger glogger.Interface) (*gorm.DB, mock.Sqlmock, error) { 21 db, mock, err := mock.New() 22 if err != nil { 23 return nil, nil, err 24 } 25 // Mock SQLite3 DB connection, the sqlite version query is essential for go-sqlite driver. 26 mock.ExpectQuery(`select sqlite_version()`). 27 WithArgs(). 28 WillReturnRows(mock.NewRows([]string{"sqlite_version()"}). 29 AddRow("3.38.0")) 30 gdb, err := gorm.Open(sqlite.Dialector{ 31 DriverName: sqlite.DriverName, 32 Conn: db, // DSN is free. IP, port, username and password is free too. 33 }, &gorm.Config{ 34 Logger: logger, 35 }) 36 if err != nil { 37 return nil, nil, err 38 } 39 // gdb is gorm db connection to the sql mock. 40 return gdb, mock, nil 41 } 42 43 func TestGormXLogger_Sqlite3(t *testing.T) { 44 var ( 45 parentLogger XLogger = nil 46 logger *GormXLogger = nil 47 ) 48 opts := []XLoggerOption{ 49 WithXLoggerLevel(LogLevelDebug), 50 WithXLoggerEncoder(JSON), 51 WithXLoggerTimeEncoder(zapcore.ISO8601TimeEncoder), 52 WithXLoggerLevelEncoder(zapcore.CapitalLevelEncoder), 53 } 54 parentLogger = NewXLogger(opts...) 55 logger = NewGormXLogger(parentLogger, 56 WithGormXLoggerIgnoreRecord404Err(), 57 WithGormXLoggerLogLevel(glogger.Info), 58 WithGormXLoggerSlowThreshold(200*time.Millisecond), 59 ) 60 61 db, mock, err := genDBMock(logger) 62 require.NoError(t, err) 63 64 type fields struct { 65 client *gorm.DB 66 } 67 type args struct { 68 } 69 testcases := []struct { 70 name string 71 fields fields 72 args args 73 invoke func(args) 74 exec func(*testing.T, args, *gorm.DB) 75 }{ 76 { 77 name: "create tbl", 78 fields: fields{ 79 client: db, 80 }, 81 args: args{}, 82 invoke: func(args args) { 83 // Mock the SQL by pattern string with dynamic value to match gorm SQL request to be executed really. 84 // Mock the db transaction start. 85 mock.ExpectBegin().WillReturnError(nil) 86 mock.ExpectExec(`SAVEPOINT create-obj`).WithArgs().WillReturnResult(driver.ResultNoRows) 87 // Mock the db transaction commit without error. 88 mock.ExpectCommit().WillReturnError(nil) 89 }, 90 exec: func(tt *testing.T, args args, client *gorm.DB) { 91 sp := "create-obj" 92 tx := client.Begin(&sql.TxOptions{ 93 Isolation: sql.LevelDefault, 94 ReadOnly: false, 95 }).SavePoint(sp) 96 err := tx.Commit().Error 97 require.NoError(tt, err) 98 }, 99 }, 100 } 101 for _, tc := range testcases { 102 t.Run(tc.name, func(tt *testing.T) { 103 tc.invoke(tc.args) 104 tc.exec(tt, tc.args, tc.fields.client) 105 }) 106 } 107 _ = parentLogger.Sync() 108 } 109 110 func TestGormXLogger_AllAPIs(t *testing.T) { 111 var ( 112 parentLogger XLogger = nil 113 logger *GormXLogger = nil 114 ) 115 opts := []XLoggerOption{ 116 WithXLoggerLevel(LogLevelDebug), 117 WithXLoggerEncoder(JSON), 118 WithXLoggerTimeEncoder(zapcore.ISO8601TimeEncoder), 119 WithXLoggerLevelEncoder(zapcore.CapitalLevelEncoder), 120 } 121 parentLogger = NewXLogger(opts...) 122 logger = NewGormXLogger(parentLogger, 123 WithGormXLoggerIgnoreRecord404Err(), 124 WithGormXLoggerLogLevel(glogger.Info), 125 WithGormXLoggerParameterizedQueries(), 126 ) 127 128 require.Equal(t, zap.ErrorLevel, getLogLevelOrDefaultForGorm(glogger.Error)) 129 require.Equal(t, zap.WarnLevel, getLogLevelOrDefaultForGorm(glogger.Warn)) 130 require.Equal(t, zap.InfoLevel, getLogLevelOrDefaultForGorm(glogger.Info)) 131 require.Equal(t, zap.DebugLevel, getLogLevelOrDefaultForGorm(glogger.Silent)) 132 133 logger.Info(context.TODO(), "sql %s", "insert into abc values(1,2,3)") 134 logger.Warn(context.TODO(), "sql %s", "insert into abc values(1,2,3)") 135 logger.Error(context.TODO(), "sql %s", "insert into abc values(1,2,3)") 136 logger.Trace(context.TODO(), time.Now(), func() (string, int64) { 137 return "insert into abc values(1,2,3)", -1 138 }, nil) 139 logger.Trace(context.TODO(), time.Now(), func() (string, int64) { 140 return "insert into abc values(1,2,3)", 1 141 }, nil) 142 logger.Trace(context.TODO(), time.Now(), func() (string, int64) { 143 return "insert into abc values(1,2,3)", -1 144 }, errors.New("insert error")) 145 logger.Trace(context.TODO(), time.Now(), func() (string, int64) { 146 return "insert into abc values(1,2,3)", 1 147 }, errors.New("insert error")) 148 logger.Trace(context.TODO(), time.Now().Add(-600*time.Millisecond), func() (string, int64) { 149 return "insert into abc values(1,2,3)", -1 150 }, nil) 151 logger.Trace(context.TODO(), time.Now().Add(-550*time.Millisecond), func() (string, int64) { 152 return "insert into abc values(1,2,3)", 1 153 }, nil) 154 _ = parentLogger.Sync() 155 logger.LogMode(glogger.Silent).Trace(context.TODO(), time.Now().Add(-500*time.Millisecond), func() (string, int64) { 156 return "insert into abc values(1,2,3)", 1 157 }, nil) 158 _ = parentLogger.Sync() 159 }