github.com/XiaoMi/Gaea@v1.2.5/backend/direct_connection_test.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package backend 15 16 import ( 17 "bytes" 18 "github.com/XiaoMi/Gaea/mysql" 19 "github.com/XiaoMi/Gaea/util/mocks/pipeTest" 20 "github.com/stretchr/testify/require" 21 "strings" 22 "testing" 23 ) 24 25 func TestAppendSetVariable(t *testing.T) { 26 var buf bytes.Buffer 27 appendSetVariable(&buf, "charset", "utf8") 28 t.Log(buf.String()) 29 appendSetVariable(&buf, "autocommit", 1) 30 t.Log(buf.String()) 31 appendSetVariableToDefault(&buf, "sql_mode") 32 t.Log(buf.String()) 33 } 34 35 func TestAppendSetVariable2(t *testing.T) { 36 var buf bytes.Buffer 37 appendSetCharset(&buf, "utf8", "utf8_general_ci") 38 t.Log(buf.String()) 39 appendSetVariable(&buf, "autocommit", 1) 40 t.Log(buf.String()) 41 appendSetVariableToDefault(&buf, "sql_mode") 42 t.Log(buf.String()) 43 } 44 45 var ( 46 // preparation 准备数据库的回应资料 47 48 // The initial handshake packet from MariaDB to Gaea 49 // 第一个交握讯息,由 MariaDB 传送欢迎讯息到 Gaea 50 mysqlInitHandShakeFirstResponseFromMaraiadbToGaea = []uint8{ 51 // length. 资料长度 52 93, 0, 0, 53 // the increment numbers. 自增串行号码 54 0, 55 56 // 93 bytes of data. 以下 93 笔数据 57 58 // protocol 协定版本 59 10, 60 // version 数据库 版本 61 53, 46, 53, 46, 53, 62 45, 49, 48, 46, 53, 63 46, 49, 50, 45, 77, 64 97, 114, 105, 97, 68, 65 66, 45, 108, 111, 103, 66 // terminated 数据库的版本结尾 67 0, 68 // connection id 连线编号 69 16, 0, 0, 0, 70 // The first scramble. 第一部份的 scramble 71 81, 64, 43, 85, 76, 90, 97, 91, 72 // reserved byte 保留数据 73 0, 74 // capability 取得功能标志 75 254, 247, 76 // charset 数据库编码 77 33, 78 // status 服务器状态 79 2, 0, 80 // capability 延伸的功能标志 81 255, 129, 82 // auth 资料和保留值 83 21, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 84 // the second scramble. 延申的 scramble 85 34, 53, 36, 85, 86 93, 86, 117, 105, 87 49, 87, 65, 125, 88 // unused data. 其他未用到的资料 89 0, 109, 121, 115, 113, 108, 95, 110, 97, 116, 90 105, 118, 101, 95, 112, 97, 115, 115, 119, 111, 91 114, 100, 0, 92 } 93 ) 94 95 // TestDirectConnWithoutDB is to test the initial handshake packet. The test doesn't use MariaDB. 96 // TestDirectConnWithoutDB 为测试数据库的后端连线流程,以下测试不使用 MariaDB 的服务器,只是单纯的单元测试 97 func TestDirectConnWithoutDB(t *testing.T) { 98 // check every step in the handshake process. 99 // 开始正式测试,把每次的交握连接进行解测试 100 101 // create the mock object 102 // 先产生模拟对象 103 mockGaea, mockMariaDB := pipeTest.NewDcServerClient(t, nil) // create the mock object for Game and MariaDB. 产生 Gaea 和 MariaDB 模拟对象 104 var dc DirectConnection // create the dc object and receive MariaDB's greet message. 对象 dc 会产生回应欢迎讯息给数据库 105 106 // the first step 107 // 交握第一步 Step1 测试数据库后端连线的初始交握 108 t.Run("test the initial handshake", func(t *testing.T) { 109 // start mocking, send message from MariaDB to Gaea. 110 // 开始进行模拟,方向为 MariaDB 欢迎 Gaea 111 mockMariaDB.SendOrReceiveMsg(mysqlInitHandShakeFirstResponseFromMaraiadbToGaea) // This message is mysqlInitHandShakeFirstResponseFromMaraiadbToGaea. 对象 mockMariaDB 会回传数据库资讯给 mockGaea,而讯息内容为 mysqlInitHandShakeFirstResponseFromMaraiadbToGaea,内容包含数据库资讯 112 113 // create MariaDB object 114 // 产生 MariaDB dc 直连对象 (用以下内容取代 reply() 函数 !) 115 var connForReceivingMsgFromMariadb = mysql.NewConn(mockGaea.GetConnRead()) // wait for sending message completely. 等一下 MariaDB 数据库会把交握讯息传送到这 116 // mysql.NewConn initializes net.conn and bufferedReader. 117 // mysql.NewConn 会同时初始化 读取连接 net.conn 和 读取缓存 bufferedReader 118 dc.conn = connForReceivingMsgFromMariadb // dc connects to test environment. 这时 dc 的连接 接通 整个测试环境 119 err := dc.readInitialHandshake() // Mock Gaea object reads the message. 模拟 Gaea 进行交握解析 120 require.Equal(t, err, nil) 121 122 // wait for sending messages to the pipe completely and reset the pipe. 123 // 等待和确认资料已经写入 pipe 并单方向重置模拟对象 124 err = mockMariaDB.WaitAndReset(mockGaea) 125 require.Equal(t, err, nil) 126 127 // the final check 128 // 计算后的检查 129 require.Equal(t, dc.capability, uint32(2181036030)) // capability 检查功能标志 130 require.Equal(t, dc.conn.ConnectionID, uint32(16)) // connection id 检查连线编号 131 require.Equal(t, dc.salt, []uint8{81, 64, 43, 85, 76, 90, 97, 91, 34, 53, 36, 85, 93, 86, 117, 105, 49, 87, 65, 125}) // Salt 检查 132 require.Equal(t, dc.status, mysql.ServerStatusAutocommit) // Status 检查服务器状态 133 }) 134 135 // the second step 136 // 交握第二步 Step2 测试数据库后端连线初始交握后的回应 137 t.Run("test response to the initial handshake", func(t *testing.T) { 138 // make a response to MariaDB after receiving the initial handshake packet. 139 // 开始进行模拟,方向为 Gaea 回应 MariaDB 欢迎 140 141 // create MariaDB object 142 // 产生 Mysql dc 直连对象 (用以下内容取代 reply() 函数 !) 143 var connForSengingMsgToMariadb = mysql.NewConn(mockGaea.GetConnWrite()) // make the response to MariaDB's greet message here. 等一下会把要回应给 MariaDB 数据库回应的欢迎讯息写入到这里 144 145 // mysql.NewConn initializes net.conn and bufferReader. However, bufferReader won't affect this result. 146 // mysql.NewConn 会同时初始化 写入连接 net.conn 和 写入缓存 bufferReader,但这里 bufferReader 将不会用到,所以不会影响测试 147 dc.conn = connForSengingMsgToMariadb // dc connects to test environment. 对象 dc 和整个测试进行连接 148 dc.conn.StartWriterBuffering() // initialize Gaea's bufferReader. 初始化 Gaea 的 写入缓存 bufferReader 149 // Isolating bufferReader and no using StartWriterBuffering() in testing will be better. However, I cannot. 150 // 最好的状况是 Gaea 的 写入缓存 bufferReader 和这个测试整个分离,但目前现有代码的函数不支持,又不想修改现在代码,所以就把 写入缓存 捉进来一起测试 151 152 // fulfill the dc object, including user, password, etc. 153 // 填入 Gaea 用户的资讯,包含 密码 154 dc.user = "xiaomi" // user 帐户名称 155 dc.password = "12345" // password 密码 156 dc.charset = "utf8mb4" // charset 数据库编码 157 dc.collation = 46 // collation 文本排序 158 159 // use the anonymous function to send the message. 160 // 使用支持使用匿名函数传送讯息 161 responseMsg := mockGaea.UseAnonymousFuncSendMsg( 162 // start using anonymous function 163 // 自订的匿名函数开始 164 func() { 165 err := dc.writeHandshakeResponse41() // write the response message. 写入回应讯息 166 require.Equal(t, err, nil) 167 err = dc.conn.Flush() // flush dc connection. 移动讯息由缓存到 Pipe 168 require.Equal(t, err, nil) 169 err = mockGaea.GetConnWrite().Close() // close the pipe. 关闭连线 170 require.Equal(t, err, nil) 171 }, 172 // use anonymous function completely. 173 // 自订的匿名函数结束 174 ).CheckArrivedMsg(mockMariaDB) // get the arrived message and check. 对传送到达对方的讯息取出进行确认 175 176 // check result. 确认结果 177 require.Equal(t, len(responseMsg), 64) // check length of the packet. 确认封包长度 178 179 require.Equal(t, strings.Contains(string(responseMsg), "xiaomi"), true) // check the existence of the account in the packet. 确认 用户帐户 是否真的写到封包里 180 181 scramble := mysql.CalcPassword(dc.salt, []byte("12345")) // calculate token. 计算 token 182 require.Equal(t, strings.Contains(string(responseMsg), string(scramble)), true) // check the existence of the token in the packet. 确认 token 是否真的写到封包里 183 184 require.Equal(t, strings.Contains(string(responseMsg), dc.password), false) // check the non-existence of the password in the packet. 确认 密码 不行写到封包里 185 186 }) 187 }