github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mysql/manager_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package mysql 21 22 import ( 23 . "github.com/onsi/ginkgo/v2" 24 . "github.com/onsi/gomega" 25 "github.com/spf13/viper" 26 27 "github.com/1aal/kubeblocks/pkg/lorry/engines" 28 ) 29 30 const ( 31 urlWithPort = "root:@tcp(127.0.0.1:3306)/mysql?multiStatements=true" 32 urlWithNoPort = "root:@tcp(127.0.0.1)/mysql?multiStatements=true" 33 ) 34 35 // Test case for Init() function 36 var _ = Describe("MySQL DBManager", func() { 37 // Set up relevant viper config variables 38 viper.Set("KB_SERVICE_USER", "testuser") 39 viper.Set("KB_SERVICE_PASSWORD", "testpassword") 40 Context("new db manager", func() { 41 It("with rigth configurations", func() { 42 properties := engines.Properties{ 43 "url": urlWithPort, 44 } 45 dbManger, err := NewManager(properties) 46 Expect(err).Should(Succeed()) 47 Expect(dbManger).ShouldNot(BeNil()) 48 }) 49 50 It("with wrong configurations", func() { 51 properties := engines.Properties{ 52 "url": "wrong-url-format", 53 } 54 dbManger, err := NewManager(properties) 55 Expect(err).Should(HaveOccurred()) 56 Expect(dbManger).Should(BeNil()) 57 }) 58 }) 59 }) 60 61 // func TestGetRole(t *testing.T) { 62 // mysqlOps, mock, _ := mockDatabase(t) 63 // 64 // t.Run("GetRole succeed", func(t *testing.T) { 65 // col1 := sqlmock.NewColumn("CURRENT_LEADER").OfType("VARCHAR", "") 66 // col2 := sqlmock.NewColumn("ROLE").OfType("VARCHAR", "") 67 // col3 := sqlmock.NewColumn("SERVER_ID").OfType("INT", 0) 68 // rows := sqlmock.NewRowsWithColumnDefinition(col1, col2, col3).AddRow("wesql-main-1.wesql-main-headless:13306", "Follower", 1) 69 // mock.ExpectQuery("select .* from information_schema.wesql_cluster_local").WillReturnRows(rows) 70 // 71 // role, err := mysqlOps.GetRole(context.Background(), &ProbeRequest{}, &ProbeResponse{}) 72 // assert.Nil(t, err) 73 // assert.Equal(t, "Follower", role) 74 // }) 75 // 76 // t.Run("GetRole fails", func(t *testing.T) { 77 // mock.ExpectQuery("select .* from information_schema.wesql_cluster_local").WillReturnError(errors.New("no record")) 78 // 79 // role, err := mysqlOps.GetRole(context.Background(), &ProbeRequest{}, &ProbeResponse{}) 80 // assert.Equal(t, "", role) 81 // assert.NotNil(t, err) 82 // }) 83 // } 84 // 85 // func TestGetLagOps(t *testing.T) { 86 // mysqlOps, mock, _ := mockDatabase(t) 87 // req := &ProbeRequest{} 88 // 89 // t.Run("GetLagOps succeed", func(t *testing.T) { 90 // col1 := sqlmock.NewColumn("CURRENT_LEADER").OfType("VARCHAR", "") 91 // col2 := sqlmock.NewColumn("ROLE").OfType("VARCHAR", "") 92 // col3 := sqlmock.NewColumn("SERVER_ID").OfType("INT", 0) 93 // rows := sqlmock.NewRowsWithColumnDefinition(col1, col2, col3).AddRow("wesql-main-1.wesql-main-headless:13306", "Follower", 1) 94 // getRoleRows := sqlmock.NewRowsWithColumnDefinition(col1, col2, col3).AddRow("wesql-main-1.wesql-main-headless:13306", "Follower", 1) 95 // if mysqlOps.OriRole == "" { 96 // mock.ExpectQuery("select .* from information_schema.wesql_cluster_local").WillReturnRows(getRoleRows) 97 // } 98 // mock.ExpectQuery("show slave status").WillReturnRows(rows) 99 // 100 // result, err := mysqlOps.GetLagOps(context.Background(), req, &ProbeResponse{}) 101 // assert.NoError(t, err) 102 // 103 // // Assert that the event and message are correct 104 // event, ok := result["event"] 105 // assert.True(t, ok) 106 // assert.Equal(t, util.OperationSuccess, event) 107 // }) 108 // } 109 // 110 // func TestQueryOps(t *testing.T) { 111 // mysqlOps, mock, _ := mockDatabase(t) 112 // req := &ProbeRequest{Metadata: map[string]string{}} 113 // req.Metadata["sql"] = "select .* from information_schema.wesql_cluster_local" 114 // 115 // t.Run("QueryOps succeed", func(t *testing.T) { 116 // col1 := sqlmock.NewColumn("CURRENT_LEADER").OfType("VARCHAR", "") 117 // col2 := sqlmock.NewColumn("ROLE").OfType("VARCHAR", "") 118 // col3 := sqlmock.NewColumn("SERVER_ID").OfType("INT", 0) 119 // rows := sqlmock.NewRowsWithColumnDefinition(col1, col2, col3).AddRow("wesql-main-1.wesql-main-headless:13306", "Follower", 1) 120 // mock.ExpectQuery("select .* from information_schema.wesql_cluster_local").WillReturnRows(rows) 121 // 122 // result, err := mysqlOps.QueryOps(context.Background(), req, &ProbeResponse{}) 123 // assert.NoError(t, err) 124 // 125 // // Assert that the event and message are correct 126 // event, ok := result["event"] 127 // assert.True(t, ok) 128 // assert.Equal(t, util.OperationSuccess, event) 129 // 130 // message, ok := result["message"] 131 // assert.True(t, ok) 132 // t.Logf("query message: %s", message) 133 // }) 134 // 135 // t.Run("QueryOps fails", func(t *testing.T) { 136 // mock.ExpectQuery("select .* from information_schema.wesql_cluster_local").WillReturnError(errors.New("no record")) 137 // 138 // result, err := mysqlOps.QueryOps(context.Background(), req, &ProbeResponse{}) 139 // assert.NoError(t, err) 140 // 141 // // Assert that the event and message are correct 142 // event, ok := result["event"] 143 // assert.True(t, ok) 144 // assert.Equal(t, util.OperationFailed, event) 145 // 146 // message, ok := result["message"] 147 // assert.True(t, ok) 148 // t.Logf("query message: %s", message) 149 // }) 150 // } 151 // 152 // func TestExecOps(t *testing.T) { 153 // mysqlOps, mock, _ := mockDatabase(t) 154 // req := &ProbeRequest{Metadata: map[string]string{}} 155 // req.Metadata["sql"] = "INSERT INTO foo (id, v1, ts) VALUES (1, 'test-1', '2021-01-22')" 156 // 157 // t.Run("ExecOps succeed", func(t *testing.T) { 158 // mock.ExpectExec("INSERT INTO foo \\(id, v1, ts\\) VALUES \\(.*\\)").WillReturnResult(sqlmock.NewResult(1, 1)) 159 // 160 // result, err := mysqlOps.ExecOps(context.Background(), req, &ProbeResponse{}) 161 // assert.NoError(t, err) 162 // 163 // // Assert that the event and message are correct 164 // event, ok := result["event"] 165 // assert.True(t, ok) 166 // assert.Equal(t, util.OperationSuccess, event) 167 // 168 // count, ok := result["count"] 169 // assert.True(t, ok) 170 // assert.Equal(t, int64(1), count.(int64)) 171 // }) 172 // 173 // t.Run("ExecOps fails", func(t *testing.T) { 174 // mock.ExpectExec("INSERT INTO foo \\(id, v1, ts\\) VALUES \\(.*\\)").WillReturnError(errors.New("insert error")) 175 // 176 // result, err := mysqlOps.ExecOps(context.Background(), req, &ProbeResponse{}) 177 // assert.NoError(t, err) 178 // 179 // // Assert that the event and message are correct 180 // event, ok := result["event"] 181 // assert.True(t, ok) 182 // assert.Equal(t, util.OperationFailed, event) 183 // 184 // message, ok := result["message"] 185 // assert.True(t, ok) 186 // t.Logf("exec error message: %s", message) 187 // }) 188 // } 189 // 190 // func TestCheckStatusOps(t *testing.T) { 191 // ctx := context.Background() 192 // req := &ProbeRequest{} 193 // resp := &ProbeResponse{Metadata: map[string]string{}} 194 // mysqlOps, mock, _ := mockDatabase(t) 195 // 196 // t.Run("Check follower", func(t *testing.T) { 197 // mysqlOps.OriRole = "follower" 198 // col1 := sqlmock.NewColumn("id").OfType("BIGINT", 1) 199 // col2 := sqlmock.NewColumn("type").OfType("BIGINT", 1) 200 // col3 := sqlmock.NewColumn("check_ts").OfType("TIME", time.Now()) 201 // rows := sqlmock.NewRowsWithColumnDefinition(col1, col2, col3). 202 // AddRow(1, 1, time.Now()) 203 // 204 // roSQL := fmt.Sprintf(`select check_ts from kb_health_check where type=%d limit 1;`, component.CheckStatusType) 205 // mock.ExpectQuery(roSQL).WillReturnRows(rows) 206 // // Call CheckStatusOps 207 // result, err := mysqlOps.CheckStatusOps(ctx, req, resp) 208 // assert.NoError(t, err) 209 // 210 // // Assert that the event and message are correct 211 // event, ok := result["event"] 212 // assert.True(t, ok) 213 // assert.Equal(t, util.OperationSuccess, event) 214 // 215 // message, ok := result["message"] 216 // assert.True(t, ok) 217 // t.Logf("check status message: %s", message) 218 // }) 219 // 220 // t.Run("Check leader", func(t *testing.T) { 221 // mysqlOps.OriRole = "leader" 222 // rwSQL := fmt.Sprintf(`begin; 223 // create table if not exists kb_health_check(type int, check_ts bigint, primary key(type)); 224 // insert into kb_health_check values(%d, now()) on duplicate key update check_ts = now(); 225 // commit; 226 // select check_ts from kb_health_check where type=%d limit 1;`, component.CheckStatusType, component.CheckStatusType) 227 // mock.ExpectExec(regexp.QuoteMeta(rwSQL)).WillReturnResult(sqlmock.NewResult(1, 1)) 228 // // Call CheckStatusOps 229 // result, err := mysqlOps.CheckStatusOps(ctx, req, resp) 230 // assert.NoError(t, err) 231 // 232 // // Assert that the event and message are correct 233 // event, ok := result["event"] 234 // assert.True(t, ok) 235 // assert.Equal(t, util.OperationSuccess, event) 236 // 237 // message, ok := result["message"] 238 // assert.True(t, ok) 239 // t.Logf("check status message: %s", message) 240 // }) 241 // 242 // t.Run("Role not configured", func(t *testing.T) { 243 // mysqlOps.OriRole = "leader1" 244 // // Call CheckStatusOps 245 // result, err := mysqlOps.CheckStatusOps(ctx, req, resp) 246 // assert.NoError(t, err) 247 // 248 // // Assert that the event and message are correct 249 // event, ok := result["event"] 250 // assert.True(t, ok) 251 // assert.Equal(t, util.OperationSuccess, event) 252 // 253 // message, ok := result["message"] 254 // assert.True(t, ok) 255 // assert.True(t, strings.HasPrefix(message.(string), "unknown access mode for role")) 256 // t.Logf("check status message: %s", message) 257 // }) 258 // 259 // t.Run("Check failed", func(t *testing.T) { 260 // mysqlOps.OriRole = "leader" 261 // rwSQL := fmt.Sprintf(`begin; 262 // create table if not exists kb_health_check(type int, check_ts bigint, primary key(type)); 263 // insert into kb_health_check values(%d, now()) on duplicate key update check_ts = now(); 264 // commit; 265 // select check_ts from kb_health_check where type=%d limit 1;`, component.CheckStatusType, component.CheckStatusType) 266 // mock.ExpectExec(regexp.QuoteMeta(rwSQL)).WillReturnError(errors.New("insert error")) 267 // // Call CheckStatusOps 268 // result, err := mysqlOps.CheckStatusOps(ctx, req, resp) 269 // assert.NoError(t, err) 270 // 271 // // Assert that the event and message are correct 272 // event, ok := result["event"] 273 // assert.True(t, ok) 274 // assert.Equal(t, util.OperationFailed, event) 275 // 276 // message, ok := result["message"] 277 // assert.True(t, ok) 278 // t.Logf("check status message: %s", message) 279 // }) 280 // } 281 // 282 // func TestMySQLAccounts(t *testing.T) { 283 // ctx := context.Background() 284 // resp := &ProbeResponse{} 285 // mysqlOps, mock, _ := mockDatabase(t) 286 // 287 // const ( 288 // userName = "turning" 289 // password = "red" 290 // roleName = "readOnly" 291 // ) 292 // t.Run("Create account", func(t *testing.T) { 293 // var err error 294 // var result OpsResult 295 // 296 // req := &ProbeRequest{} 297 // req.Operation = util.CreateUserOp 298 // req.Metadata = map[string]string{} 299 // 300 // result, err = mysqlOps.createUserOps(ctx, req, resp) 301 // assert.Nil(t, err) 302 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 303 // assert.Equal(t, ErrNoUserName.Error(), result[util.RespFieldMessage]) 304 // 305 // req.Metadata["userName"] = userName 306 // result, err = mysqlOps.createUserOps(ctx, req, resp) 307 // assert.Nil(t, err) 308 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 309 // assert.Equal(t, ErrNoPassword.Error(), result[util.RespFieldMessage]) 310 // 311 // req.Metadata["password"] = password 312 // 313 // createUserCmd := fmt.Sprintf("CREATE USER '%s'@'%%' IDENTIFIED BY '%s';", req.Metadata["userName"], req.Metadata["password"]) 314 // mock.ExpectExec(createUserCmd).WillReturnResult(sqlmock.NewResult(1, 1)) 315 // result, err = mysqlOps.createUserOps(ctx, req, resp) 316 // assert.Nil(t, err) 317 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 318 // }) 319 // 320 // t.Run("Delete account", func(t *testing.T) { 321 // var err error 322 // var result OpsResult 323 // 324 // req := &ProbeRequest{} 325 // req.Operation = util.CreateUserOp 326 // req.Metadata = map[string]string{} 327 // 328 // result, err = mysqlOps.deleteUserOps(ctx, req, resp) 329 // assert.Nil(t, err) 330 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 331 // assert.Equal(t, ErrNoUserName.Error(), result[util.RespFieldMessage]) 332 // 333 // req.Metadata["userName"] = userName 334 // deleteUserCmd := fmt.Sprintf("DROP USER IF EXISTS '%s'@'%%';", req.Metadata["userName"]) 335 // mock.ExpectExec(deleteUserCmd).WillReturnResult(sqlmock.NewResult(1, 1)) 336 // 337 // result, err = mysqlOps.deleteUserOps(ctx, req, resp) 338 // assert.Nil(t, err) 339 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 340 // }) 341 // t.Run("Describe account", func(t *testing.T) { 342 // var err error 343 // var result OpsResult 344 // 345 // req := &ProbeRequest{} 346 // req.Operation = util.CreateUserOp 347 // req.Metadata = map[string]string{} 348 // 349 // col1 := sqlmock.NewColumn("Grants for "+userName+"@%").OfType("STRING", "turning") 350 // rows := sqlmock.NewRowsWithColumnDefinition(col1).AddRow(readOnlyRPriv) 351 // 352 // result, err = mysqlOps.describeUserOps(ctx, req, resp) 353 // assert.Nil(t, err) 354 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 355 // assert.Equal(t, ErrNoUserName.Error(), result[util.RespFieldMessage]) 356 // 357 // req.Metadata["userName"] = userName 358 // 359 // showGrantTpl := "SHOW GRANTS FOR '%s'@'%%';" 360 // descUserCmd := fmt.Sprintf(showGrantTpl, req.Metadata["userName"]) 361 // mock.ExpectQuery(descUserCmd).WillReturnRows(rows) 362 // 363 // result, err = mysqlOps.describeUserOps(ctx, req, resp) 364 // assert.Nil(t, err) 365 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent]) 366 // 367 // data := result[util.RespFieldMessage].(string) 368 // users := []util.UserInfo{} 369 // err = json.Unmarshal([]byte(data), &users) 370 // assert.Nil(t, err) 371 // assert.Equal(t, 1, len(users)) 372 // assert.Equal(t, userName, users[0].UserName) 373 // assert.NotEmpty(t, users[0].RoleName) 374 // assert.True(t, util.ReadOnlyRole.EqualTo(users[0].RoleName)) 375 // }) 376 // 377 // t.Run("List accounts", func(t *testing.T) { 378 // var err error 379 // var result OpsResult 380 // 381 // req := &ProbeRequest{} 382 // req.Operation = util.CreateUserOp 383 // req.Metadata = map[string]string{} 384 // 385 // col1 := sqlmock.NewColumn("userName").OfType("STRING", "turning") 386 // col2 := sqlmock.NewColumn("expired").OfType("STRING", "T") 387 // 388 // rows := sqlmock.NewRowsWithColumnDefinition(col1, col2). 389 // AddRow(userName, "T").AddRow("testuser", "F") 390 // 391 // listUserCmd := "SELECT user AS userName, CASE password_expired WHEN 'N' THEN 'F' ELSE 'T' END as expired FROM mysql.user WHERE host = '%' and user <> 'root' and user not like 'kb%';" 392 // mock.ExpectQuery(regexp.QuoteMeta(listUserCmd)).WillReturnRows(rows) 393 // 394 // result, err = mysqlOps.listUsersOps(ctx, req, resp) 395 // assert.Nil(t, err) 396 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 397 // data := result[util.RespFieldMessage].(string) 398 // users := []util.UserInfo{} 399 // err = json.Unmarshal([]byte(data), &users) 400 // assert.Nil(t, err) 401 // assert.Equal(t, 2, len(users)) 402 // assert.Equal(t, userName, users[0].UserName) 403 // }) 404 // 405 // t.Run("Grant Roles", func(t *testing.T) { 406 // var err error 407 // var result OpsResult 408 // 409 // req := &ProbeRequest{} 410 // req.Operation = util.CreateUserOp 411 // req.Metadata = map[string]string{} 412 // 413 // result, err = mysqlOps.grantUserRoleOps(ctx, req, resp) 414 // assert.Nil(t, err) 415 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 416 // assert.Equal(t, ErrNoUserName.Error(), result[util.RespFieldMessage]) 417 // 418 // req.Metadata["userName"] = userName 419 // result, err = mysqlOps.grantUserRoleOps(ctx, req, resp) 420 // assert.Nil(t, err) 421 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 422 // assert.Equal(t, ErrNoRoleName.Error(), result[util.RespFieldMessage]) 423 // 424 // req.Metadata["roleName"] = roleName 425 // roleDesc, err := mysqlOps.role2Priv(req.Metadata["roleName"]) 426 // assert.Nil(t, err) 427 // grantRoleCmd := fmt.Sprintf("GRANT %s TO '%s'@'%%';", roleDesc, req.Metadata["userName"]) 428 // 429 // mock.ExpectExec(grantRoleCmd).WillReturnResult(sqlmock.NewResult(1, 1)) 430 // result, err = mysqlOps.grantUserRoleOps(ctx, req, resp) 431 // assert.Nil(t, err) 432 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 433 // }) 434 // 435 // t.Run("Revoke Roles", func(t *testing.T) { 436 // var err error 437 // var result OpsResult 438 // 439 // req := &ProbeRequest{} 440 // req.Operation = util.CreateUserOp 441 // req.Metadata = map[string]string{} 442 // 443 // result, err = mysqlOps.revokeUserRoleOps(ctx, req, resp) 444 // assert.Nil(t, err) 445 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 446 // assert.Equal(t, ErrNoUserName.Error(), result[util.RespFieldMessage]) 447 // 448 // req.Metadata["userName"] = userName 449 // result, err = mysqlOps.revokeUserRoleOps(ctx, req, resp) 450 // assert.Nil(t, err) 451 // assert.Equal(t, util.RespEveFail, result[util.RespFieldEvent]) 452 // assert.Equal(t, ErrNoRoleName.Error(), result[util.RespFieldMessage]) 453 // 454 // req.Metadata["roleName"] = roleName 455 // roleDesc, err := mysqlOps.role2Priv(req.Metadata["roleName"]) 456 // assert.Nil(t, err) 457 // revokeRoleCmd := fmt.Sprintf("REVOKE %s FROM '%s'@'%%';", roleDesc, req.Metadata["userName"]) 458 // 459 // mock.ExpectExec(revokeRoleCmd).WillReturnResult(sqlmock.NewResult(1, 1)) 460 // result, err = mysqlOps.revokeUserRoleOps(ctx, req, resp) 461 // assert.Nil(t, err) 462 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 463 // }) 464 // t.Run("List System Accounts", func(t *testing.T) { 465 // var err error 466 // var result OpsResult 467 // 468 // req := &ProbeRequest{} 469 // req.Operation = util.CreateUserOp 470 // req.Metadata = map[string]string{} 471 // 472 // col1 := sqlmock.NewColumn("userName").OfType("STRING", "turning") 473 // 474 // rows := sqlmock.NewRowsWithColumnDefinition(col1). 475 // AddRow("kbadmin") 476 // 477 // stmt := "SELECT user AS userName FROM mysql.user WHERE host = '%' and user like 'kb%';" 478 // mock.ExpectQuery(regexp.QuoteMeta(stmt)).WillReturnRows(rows) 479 // 480 // result, err = mysqlOps.listSystemAccountsOps(ctx, req, resp) 481 // assert.Nil(t, err) 482 // assert.Equal(t, util.RespEveSucc, result[util.RespFieldEvent], result[util.RespFieldMessage]) 483 // data := result[util.RespFieldMessage].(string) 484 // users := []string{} 485 // err = json.Unmarshal([]byte(data), &users) 486 // assert.Nil(t, err) 487 // assert.Equal(t, 1, len(users)) 488 // assert.Equal(t, "kbadmin", users[0]) 489 // }) 490 // } 491 // func mockDatabase(t *testing.T) (*MysqlOperations, sqlmock.Sqlmock, error) { 492 // viper.SetDefault("KB_SERVICE_ROLES", "{\"follower\":\"Readonly\",\"leader\":\"ReadWrite\"}") 493 // viper.Set("KB_POD_NAME", "test-pod-0") 494 // viper.Set(constant.KBEnvWorkloadType, "consensus") 495 // db, mock, err := sqlmock.New(sqlmock.MonitorPingsOption(true)) 496 // if err != nil { 497 // t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 498 // } 499 // 500 // properties := make(component.Properties) 501 // properties["url"] = urlWithPort 502 // mysqlOps := NewMysql() 503 // _ = mysqlOps.Init(properties) 504 // mysqlOps.Manager.(*mysql.WesqlManager).DB = db 505 // 506 // return mysqlOps, mock, err 507 // } 508 //