client_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Author: simon (ynwdlxm@163.com)
  2. // Date: 2025/10/17 14:53
  3. // Desc: 客户端单元测试
  4. package client_
  5. import (
  6. "bufio"
  7. "bytes"
  8. "net"
  9. "sync"
  10. "testing"
  11. "time"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/runningwater/go-redis/resp/reply"
  14. )
  15. // mockServer 模拟Redis服务器用于测试
  16. type mockServer struct {
  17. listener net.Listener
  18. conns []net.Conn
  19. mu sync.Mutex
  20. closed bool
  21. }
  22. // newMockServer 创建一个新的模拟服务器
  23. func newMockServer(addr string) (*mockServer, error) {
  24. listener, err := net.Listen("tcp", addr)
  25. if err != nil {
  26. return nil, err
  27. }
  28. server := &mockServer{
  29. listener: listener,
  30. conns: make([]net.Conn, 0),
  31. }
  32. go server.acceptConnections()
  33. return server, nil
  34. }
  35. // acceptConnections 接受客户端连接
  36. func (s *mockServer) acceptConnections() {
  37. for {
  38. conn, err := s.listener.Accept()
  39. if err != nil {
  40. s.mu.Lock()
  41. closed := s.closed
  42. s.mu.Unlock()
  43. if closed {
  44. return
  45. }
  46. continue
  47. }
  48. s.mu.Lock()
  49. s.conns = append(s.conns, conn)
  50. s.mu.Unlock()
  51. go s.handleConnection(conn)
  52. }
  53. }
  54. // handleConnection 处理客户端连接
  55. func (s *mockServer) handleConnection(conn net.Conn) {
  56. reader := bufio.NewReader(conn)
  57. for {
  58. // 读取客户端发送的数据
  59. line, err := reader.ReadBytes('\n')
  60. if err != nil {
  61. return
  62. }
  63. // 解析命令
  64. if bytes.Contains(line, []byte("PING")) {
  65. // 回复 PONG
  66. conn.Write([]byte("+PONG\r\n"))
  67. } else if bytes.Contains(line, []byte("SET")) {
  68. // 回复 OK
  69. conn.Write([]byte("+OK\r\n"))
  70. } else if bytes.Contains(line, []byte("GET")) {
  71. // 回复值
  72. conn.Write([]byte("$5\r\nvalue\r\n"))
  73. } else {
  74. // 默认回复 OK
  75. conn.Write([]byte("+OK\r\n"))
  76. }
  77. }
  78. }
  79. // close 关闭模拟服务器
  80. func (s *mockServer) close() {
  81. s.mu.Lock()
  82. defer s.mu.Unlock()
  83. s.closed = true
  84. s.listener.Close()
  85. for _, conn := range s.conns {
  86. conn.Close()
  87. }
  88. }
  89. // TestNewClient 测试创建新客户端
  90. func TestNewClient(t *testing.T) {
  91. // 启动模拟服务器
  92. server, err := newMockServer("localhost:0")
  93. assert.NoError(t, err)
  94. defer server.close()
  95. // 创建客户端
  96. client, err := NewClient(server.listener.Addr().String())
  97. assert.NoError(t, err)
  98. assert.NotNil(t, client)
  99. assert.Equal(t, server.listener.Addr().String(), client.addr)
  100. }
  101. // TestClientStartAndStop 测试客户端启动和停止
  102. func TestClientStartAndStop(t *testing.T) {
  103. // 启动模拟服务器
  104. server, err := newMockServer("localhost:0")
  105. assert.NoError(t, err)
  106. defer server.close()
  107. // 创建客户端
  108. client, err := NewClient(server.listener.Addr().String())
  109. assert.NoError(t, err)
  110. // 启动客户端
  111. client.Start()
  112. // 等待一段时间确保协程启动
  113. time.Sleep(100 * time.Millisecond)
  114. // 停止客户端
  115. client.Stop()
  116. }
  117. // TestClientSend 测试发送命令
  118. func TestClientSend(t *testing.T) {
  119. // 启动模拟服务器
  120. server, err := newMockServer("localhost:0")
  121. assert.NoError(t, err)
  122. defer server.close()
  123. // 创建客户端
  124. client, err := NewClient(server.listener.Addr().String())
  125. assert.NoError(t, err)
  126. defer client.Stop()
  127. // 启动客户端
  128. client.Start()
  129. // 发送 SET 命令
  130. args := [][]byte{[]byte("SET"), []byte("key"), []byte("value")}
  131. result := client.Send(args)
  132. // 检查结果
  133. assert.IsType(t, reply.NewOkReply(), result)
  134. assert.Equal(t, "OK", result.String())
  135. }
  136. // TestClientPing 测试PING命令
  137. func TestClientPing(t *testing.T) {
  138. // 启动模拟服务器
  139. server, err := newMockServer("localhost:0")
  140. assert.NoError(t, err)
  141. defer server.close()
  142. // 创建客户端
  143. client, err := NewClient(server.listener.Addr().String())
  144. assert.NoError(t, err)
  145. defer client.Stop()
  146. // 启动客户端
  147. client.Start()
  148. // 发送 PING 命令
  149. args := [][]byte{[]byte("PING")}
  150. result := client.Send(args)
  151. // 检查结果
  152. rep := reply.NewStatusReply("OK")
  153. assert.IsType(t, rep, result)
  154. assert.Equal(t, rep.String(), result.String())
  155. }
  156. // TestClientGet 测试GET命令
  157. func TestClientGet(t *testing.T) {
  158. // 启动模拟服务器
  159. server, err := newMockServer("localhost:0")
  160. assert.NoError(t, err)
  161. defer server.close()
  162. // 创建客户端
  163. client, err := NewClient(server.listener.Addr().String())
  164. assert.NoError(t, err)
  165. defer client.Stop()
  166. // 启动客户端
  167. client.Start()
  168. // 发送 GET 命令
  169. args := [][]byte{[]byte("GET"), []byte("key")}
  170. result := client.Send(args)
  171. // 检查结果
  172. repl := reply.NewStatusReply("OK")
  173. assert.IsType(t, repl, result)
  174. assert.Equal(t, repl.String(), result.String())
  175. }
  176. // TestClientStop 测试停止已停止的客户端
  177. func TestClientStopClosed(t *testing.T) {
  178. // 启动模拟服务器
  179. server, err := newMockServer("localhost:0")
  180. assert.NoError(t, err)
  181. defer server.close()
  182. // 创建客户端
  183. client, err := NewClient(server.listener.Addr().String())
  184. assert.NoError(t, err)
  185. // 停止客户端两次
  186. client.Stop()
  187. client.Stop() // 应该安全地处理重复停止
  188. }
  189. // TestClientSendOnClosed 测试向已关闭的客户端发送命令
  190. func TestClientSendOnClosed(t *testing.T) {
  191. // 启动模拟服务器
  192. server, err := newMockServer("localhost:0")
  193. assert.NoError(t, err)
  194. defer server.close()
  195. // 创建客户端
  196. client, err := NewClient(server.listener.Addr().String())
  197. assert.NoError(t, err)
  198. // 启动然后立即停止客户端
  199. client.Start()
  200. client.Stop()
  201. // 向已关闭的客户端发送命令
  202. args := [][]byte{[]byte("PING")}
  203. result := client.Send(args)
  204. // 应该返回错误
  205. assert.IsType(t, reply.NewErrReply(""), result)
  206. assert.Contains(t, result.String(), "client is closed")
  207. }
  208. // TestClientHeartbeat 测试心跳功能
  209. func TestClientHeartbeat(t *testing.T) {
  210. // 启动模拟服务器
  211. server, err := newMockServer("localhost:0")
  212. assert.NoError(t, err)
  213. defer server.close()
  214. // 创建客户端
  215. client, err := NewClient(server.listener.Addr().String())
  216. assert.NoError(t, err)
  217. defer client.Stop()
  218. // 启动客户端
  219. client.Start()
  220. // 等待心跳发生
  221. time.Sleep(12 * time.Second) // 心跳间隔是10秒
  222. // 客户端应该仍然工作正常
  223. args := [][]byte{[]byte("PING")}
  224. result := client.Send(args)
  225. assert.IsType(t, reply.NewStatusReply("OK"), result)
  226. assert.Equal(t, "+OK\r\n", result.String())
  227. }