Files
ngzz-mc/internal/netserver/netserver_test.go

127 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package netserver
import (
"context"
"net"
"testing"
"time"
"mc/internal/logx"
"mc/internal/netproto"
)
// testHandler 记录回调的测试处理器。
type testHandler struct {
logins chan netproto.Handshake
moves chan netproto.PlayerMove
connects chan uint64
}
func newTestHandler() *testHandler {
return &testHandler{
logins: make(chan netproto.Handshake, 8),
moves: make(chan netproto.PlayerMove, 8),
connects: make(chan uint64, 8),
}
}
func (h *testHandler) OnConnect(c *Conn) { h.connects <- c.ID() }
func (h *testHandler) OnHandshake(c *Conn, hs netproto.Handshake) uint8 {
h.logins <- hs
return 0
}
func (h *testHandler) OnMove(c *Conn, m netproto.PlayerMove) { h.moves <- m }
func (h *testHandler) OnDisconnect(c *Conn) {}
// startServer 启动测试服务器。
func startServer(t *testing.T) (*Server, *testHandler, context.CancelFunc) {
t.Helper()
log, err := logx.New("", logx.LevelDebug) // 仅 stderr(避免临时文件句柄)
if err != nil {
t.Fatalf("创建日志失败: %v", err)
}
h := newTestHandler()
s := New("127.0.0.1:0", log, h)
ctx, cancel := context.WithCancel(context.Background())
if err := s.Listen(ctx); err != nil {
t.Fatalf("监听失败: %v", err)
}
t.Cleanup(func() {
cancel()
time.Sleep(20 * time.Millisecond)
})
return s, h, cancel
}
// dial 建立客户端连接。
func dial(t *testing.T, addr string) net.Conn {
t.Helper()
c, err := net.DialTimeout("tcp", addr, 3*time.Second)
if err != nil {
t.Fatalf("拨号失败: %v", err)
}
t.Cleanup(func() { _ = c.Close() })
return c
}
// TestHandshakeFlow 握手 → 登录响应 → 心跳回显全链路(网络协议.md §4)。
func TestHandshakeFlow(t *testing.T) {
s, h, _ := startServer(t)
nc := dial(t, s.Addr())
// 发送握手
hs := netproto.Handshake{ProtocolVersion: 1, ClientID: "测试客户端"}
_, err := nc.Write(netproto.EncodeFrame(netproto.Frame{MsgID: netproto.MsgHandshake, Payload: hs.Encode()}))
if err != nil {
t.Fatalf("发送握手失败: %v", err)
}
select {
case got := <-h.logins:
if got != hs {
t.Fatalf("握手内容不一致: %+v", got)
}
case <-time.After(3 * time.Second):
t.Fatal("未收到握手回调")
}
// 读登录响应帧
_ = nc.SetReadDeadline(time.Now().Add(3 * time.Second))
buf := make([]byte, 256)
n, err := nc.Read(buf)
if err != nil {
t.Fatalf("读登录响应失败: %v", err)
}
f, _, err := netproto.DecodeFrame(buf[:n])
if err != nil || f.MsgID != netproto.MsgLoginResponse {
t.Fatalf("登录响应异常: %+v err=%v", f, err)
}
if len(f.Payload) != 1 || f.Payload[0] != 0 {
t.Fatalf("登录结果应为 0(成功),实际 %v", f.Payload)
}
// 心跳回显
_, _ = nc.Write(netproto.EncodeFrame(netproto.Frame{MsgID: netproto.MsgHeartbeat, Payload: []byte{7}}))
n, _ = nc.Read(buf)
f, _, _ = netproto.DecodeFrame(buf[:n])
if f.MsgID != netproto.MsgHeartbeat || len(f.Payload) != 1 || f.Payload[0] != 7 {
t.Fatalf("心跳回显异常: %+v", f)
}
}
// TestMoveBroadcast 移动消息分发。
func TestMoveBroadcast(t *testing.T) {
s, h, _ := startServer(t)
nc := dial(t, s.Addr())
m := netproto.PlayerMove{X: 1.5, Y: 64, Z: -2, Yaw: 0.5, Pitch: 0, OnGround: true}
_, _ = nc.Write(netproto.EncodeFrame(netproto.Frame{MsgID: netproto.MsgPlayerMove, Payload: m.Encode()}))
select {
case got := <-h.moves:
if got != m {
t.Fatalf("移动数据不一致: %+v", got)
}
case <-time.After(3 * time.Second):
t.Fatal("未收到移动回调")
}
_ = s.Count() // 连接计数可用
}