github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/conn/mockdb.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package conn 15 16 import ( 17 "database/sql" 18 "fmt" 19 "io" 20 "net" 21 "net/http" 22 "time" 23 24 "github.com/DATA-DOG/go-sqlmock" 25 check "github.com/pingcap/check" 26 tidbConfig "github.com/pingcap/tidb/pkg/config" 27 "github.com/pingcap/tidb/pkg/domain" 28 "github.com/pingcap/tidb/pkg/kv" 29 "github.com/pingcap/tidb/pkg/server" 30 "github.com/pingcap/tidb/pkg/session" 31 "github.com/pingcap/tidb/pkg/store/mockstore" 32 "github.com/tikv/client-go/v2/testutils" 33 ) 34 35 type mockDBProvider struct { 36 verDB *sql.DB // verDB user for show version. 37 db *sql.DB 38 // customDB defines a db that will never be close 39 // TODO: we should use customDB for all mock. 40 customDB *sql.DB 41 } 42 43 // Apply will build BaseDB with DBConfig. 44 func (d *mockDBProvider) Apply(config ScopedDBConfig) (*BaseDB, error) { 45 if d.verDB != nil { 46 if err := d.verDB.Ping(); err == nil { 47 // nolint:nilerr 48 return NewBaseDBForTest(d.verDB), nil 49 } 50 } 51 if d.customDB != nil { 52 if err := d.customDB.Ping(); err == nil { 53 // nolint:nilerr 54 return NewMockDB(d.customDB), nil 55 } 56 } 57 return NewBaseDBForTest(d.db), nil 58 } 59 60 // InitMockDB return a mocked db for unit test. 61 func InitMockDB(c *check.C) sqlmock.Sqlmock { 62 db, mock, err := sqlmock.New() 63 c.Assert(err, check.IsNil) 64 if mdbp, ok := DefaultDBProvider.(*mockDBProvider); ok { 65 mdbp.db = db 66 } else { 67 DefaultDBProvider = &mockDBProvider{db: db} 68 } 69 return mock 70 } 71 72 // MockDefaultDBProvider return a mocked db for unit test. 73 func MockDefaultDBProvider() (sqlmock.Sqlmock, error) { 74 db, mock, err := sqlmock.New() 75 if err != nil { 76 return nil, err 77 } 78 if mdbp, ok := DefaultDBProvider.(*mockDBProvider); ok { 79 mdbp.db = db 80 } else { 81 DefaultDBProvider = &mockDBProvider{db: db} 82 } 83 return mock, nil 84 } 85 86 // InitVersionDB return a mocked db for unit test's show version. 87 func InitVersionDB() sqlmock.Sqlmock { 88 // nolint:errcheck 89 db, mock, _ := sqlmock.New() 90 if mdbp, ok := DefaultDBProvider.(*mockDBProvider); ok { 91 mdbp.verDB = db 92 } else { 93 DefaultDBProvider = &mockDBProvider{verDB: db} 94 } 95 return mock 96 } 97 98 func InitMockDBFull() (*sql.DB, sqlmock.Sqlmock, error) { 99 db, mock, err := sqlmock.New() 100 if err != nil { 101 return nil, nil, err 102 } 103 if mdbp, ok := DefaultDBProvider.(*mockDBProvider); ok { 104 mdbp.db = db 105 } else { 106 DefaultDBProvider = &mockDBProvider{db: db} 107 } 108 return db, mock, err 109 } 110 111 func InitMockDBNotClose() (*sql.DB, sqlmock.Sqlmock, error) { 112 db, mock, err := sqlmock.New() 113 if err != nil { 114 return nil, nil, err 115 } 116 if mdbp, ok := DefaultDBProvider.(*mockDBProvider); ok { 117 mdbp.customDB = db 118 } else { 119 DefaultDBProvider = &mockDBProvider{customDB: db} 120 } 121 return db, mock, err 122 } 123 124 // TODO: export Config in https://github.com/pingcap/tidb/blob/a8fa29b56d633b1ec843e21cb89131dd4fd601db/br/pkg/mock/mock_cluster.go#L35 125 // Cluster is mock tidb cluster. 126 type Cluster struct { 127 *server.Server 128 testutils.Cluster 129 kv.Storage 130 *server.TiDBDriver 131 *domain.Domain 132 Port int 133 } 134 135 // NewCluster create a new mock cluster. 136 func NewCluster() (*Cluster, error) { 137 cluster := &Cluster{} 138 139 storage, err := mockstore.NewMockStore( 140 mockstore.WithClusterInspector(func(c testutils.Cluster) { 141 mockstore.BootstrapWithSingleStore(c) 142 cluster.Cluster = c 143 }), 144 ) 145 if err != nil { 146 return nil, err 147 } 148 cluster.Storage = storage 149 150 session.SetSchemaLease(0) 151 session.DisableStats4Test() 152 dom, err := session.BootstrapSession(storage) 153 if err != nil { 154 return nil, err 155 } 156 cluster.Domain = dom 157 158 return cluster, nil 159 } 160 161 // Start runs a mock cluster. 162 func (mock *Cluster) Start() error { 163 // choose a random available port 164 l1, _ := net.Listen("tcp", "127.0.0.1:") 165 statusPort := l1.Addr().(*net.TCPAddr).Port 166 167 // choose a random available port 168 l2, _ := net.Listen("tcp", "127.0.0.1:") 169 addrPort := l2.Addr().(*net.TCPAddr).Port 170 171 mock.TiDBDriver = server.NewTiDBDriver(mock.Storage) 172 cfg := tidbConfig.NewConfig() 173 cfg.Port = uint(addrPort) 174 cfg.Store = "tikv" 175 cfg.Status.StatusPort = uint(statusPort) 176 cfg.Status.ReportStatus = true 177 cfg.Socket = fmt.Sprintf("/tmp/tidb-mock-%d.sock", time.Now().UnixNano()) 178 179 // close port for next listen in NewServer 180 l1.Close() 181 l2.Close() 182 svr, err := server.NewServer(cfg, mock.TiDBDriver) 183 if err != nil { 184 return err 185 } 186 mock.Server = svr 187 mock.Server.SetDomain(mock.Domain) 188 go func() { 189 if err1 := svr.Run(nil); err1 != nil { 190 panic(err1) 191 } 192 }() 193 waitUntilServerOnline(cfg.Status.StatusPort) 194 mock.Port = addrPort 195 return nil 196 } 197 198 // Stop stops a mock cluster. 199 func (mock *Cluster) Stop() { 200 if mock.Domain != nil { 201 mock.Domain.Close() 202 } 203 if mock.Storage != nil { 204 _ = mock.Storage.Close() 205 } 206 if mock.Server != nil { 207 mock.Server.Close() 208 } 209 } 210 211 func waitUntilServerOnline(statusPort uint) { 212 // connect http status 213 statusURL := fmt.Sprintf("http://127.0.0.1:%d/status", statusPort) 214 for retry := 0; retry < 100; retry++ { 215 // nolint:gosec,noctx 216 // #nosec G107 217 resp, err := http.Get(statusURL) 218 if err == nil { 219 // Ignore errors. 220 _, _ = io.ReadAll(resp.Body) 221 _ = resp.Body.Close() 222 break 223 } 224 time.Sleep(time.Millisecond * 10) 225 } 226 }