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  }