test(http3): 完善 HTTP/3 服务器测试(覆盖率 46% → 93.1%)
补充 server_test.go 中未实现的测试用例: 新增测试: - TestNewServer_TableDriven: 表驱动验证所有 NewServer 错误/成功路径 - TestNewServer_VerifyInternalFields: 验证服务器内部字段初始化 - TestStart_AlreadyRunning: 重复启动返回 "server already running" - TestStart_InvalidListenAddress: 无效监听地址返回错误 - TestStart_Success: 绑定随机端口并验证运行状态 - TestStart_EmptyListenAddress: 空地址回退到 :443(无权限时 skip) - TestStart_QUICConfigDefaults: 零值/自定义 MaxStreams、IdleTimeout、0RTT - TestStart_MultipleStartsAndStops: start → stop → start 生命周期循环 - TestStop_NotRunning: 空闲服务器 stop 为空操作 - TestStop_Running: stop 正确设置 running = false - TestStop_CalledMultipleTimes: 重复 stop 安全 - TestStartStop_Lifecycle: 完整生命周期状态断言
This commit is contained in:
parent
f26a4a7949
commit
8bb88e8898
@ -11,13 +11,57 @@
|
||||
package http3
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/config"
|
||||
)
|
||||
|
||||
// newTestTLSConfig 创建用于测试的自签名 TLS 证书
|
||||
func newTestTLSConfig(t *testing.T) *tls.Config {
|
||||
t.Helper()
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err)
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Test"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
}
|
||||
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
|
||||
require.NoError(t, err)
|
||||
|
||||
cert := &tls.Certificate{
|
||||
Certificate: [][]byte{certDER},
|
||||
PrivateKey: key,
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{*cert},
|
||||
}
|
||||
}
|
||||
|
||||
// newTestHandler 创建测试用的 fasthttp handler
|
||||
func newTestHandler() fasthttp.RequestHandler {
|
||||
return func(_ *fasthttp.RequestCtx) {}
|
||||
}
|
||||
|
||||
// TestNewServer_NilConfig 测试空配置错误
|
||||
func TestNewServer_NilConfig(t *testing.T) {
|
||||
handler := func(_ *fasthttp.RequestCtx) {}
|
||||
@ -120,6 +164,396 @@ func TestNewServer_Success(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewServer_TableDriven 使用表驱动测试各种配置组合
|
||||
func TestNewServer_TableDriven(t *testing.T) {
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *config.HTTP3Config
|
||||
handler fasthttp.RequestHandler
|
||||
tlsConfig *tls.Config
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "空配置",
|
||||
cfg: nil,
|
||||
handler: handler,
|
||||
tlsConfig: tlsConfig,
|
||||
wantErr: true,
|
||||
errMsg: "http3 config is nil",
|
||||
},
|
||||
{
|
||||
name: "空handler",
|
||||
cfg: &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":443",
|
||||
},
|
||||
handler: nil,
|
||||
tlsConfig: tlsConfig,
|
||||
wantErr: true,
|
||||
errMsg: "handler is nil",
|
||||
},
|
||||
{
|
||||
name: "空TLS配置",
|
||||
cfg: &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":443",
|
||||
},
|
||||
handler: handler,
|
||||
tlsConfig: nil,
|
||||
wantErr: true,
|
||||
errMsg: "tls config is required for HTTP/3",
|
||||
},
|
||||
{
|
||||
name: "完整配置",
|
||||
cfg: &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":443",
|
||||
MaxStreams: 200,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
Enable0RTT: true,
|
||||
},
|
||||
handler: handler,
|
||||
tlsConfig: tlsConfig,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "最小配置",
|
||||
cfg: &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
},
|
||||
handler: handler,
|
||||
tlsConfig: tlsConfig,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "禁用状态",
|
||||
cfg: &config.HTTP3Config{
|
||||
Enabled: false,
|
||||
},
|
||||
handler: handler,
|
||||
tlsConfig: tlsConfig,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server, err := NewServer(tt.cfg, tt.handler, tt.tlsConfig)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tt.errMsg, err.Error())
|
||||
assert.Nil(t, server)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, server)
|
||||
assert.Equal(t, tt.cfg, server.config)
|
||||
assert.NotNil(t, server.handler)
|
||||
assert.NotNil(t, server.adapter)
|
||||
assert.Equal(t, tt.tlsConfig, server.tlsConfig)
|
||||
assert.False(t, server.running)
|
||||
assert.Nil(t, server.listener)
|
||||
assert.Nil(t, server.http3Server)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewServer_VerifyInternalFields 验证创建后内部字段的值
|
||||
func TestNewServer_VerifyInternalFields(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":8443",
|
||||
MaxStreams: 256,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
Enable0RTT: true,
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, server)
|
||||
|
||||
assert.Equal(t, cfg, server.config)
|
||||
assert.Equal(t, tlsConfig, server.tlsConfig)
|
||||
assert.NotNil(t, server.adapter)
|
||||
assert.False(t, server.running)
|
||||
assert.Nil(t, server.listener)
|
||||
assert.Nil(t, server.http3Server)
|
||||
}
|
||||
|
||||
// TestStart_AlreadyRunning 测试启动已运行的服务器
|
||||
func TestStart_AlreadyRunning(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
t.Cleanup(func() { _ = server.Stop() })
|
||||
|
||||
err = server.Start()
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "server already running", err.Error())
|
||||
}
|
||||
|
||||
// TestStart_InvalidListenAddress 测试无效监听地址
|
||||
func TestStart_InvalidListenAddress(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
listen string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "无效地址格式",
|
||||
listen: "not-a-valid-address:999999999",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "无效端口",
|
||||
listen: ":999999999",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: tt.listen,
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = server.Start()
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
_ = server.Stop()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestStart_Success 测试成功启动服务器(使用随机端口)
|
||||
func TestStart_Success(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
MaxStreams: 100,
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
t.Cleanup(func() { _ = server.Stop() })
|
||||
|
||||
assert.True(t, server.running)
|
||||
assert.NotNil(t, server.listener)
|
||||
assert.NotNil(t, server.http3Server)
|
||||
}
|
||||
|
||||
// TestStart_EmptyListenAddress 测试空监听地址时使用默认值
|
||||
func TestStart_EmptyListenAddress(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: "",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 默认地址是 :443,可能需要权限,这里只验证逻辑不会 panic
|
||||
// 如果绑定失败是因为权限,则跳过
|
||||
err = server.Start()
|
||||
if err != nil {
|
||||
t.Skipf("无法绑定默认端口 :443(可能需要权限): %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = server.Stop() })
|
||||
}
|
||||
|
||||
// TestStart_QUICConfigDefaults 测试 QUIC 配置默认值
|
||||
func TestStart_QUICConfigDefaults(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
maxStreams int
|
||||
idle time.Duration
|
||||
enable0RTT bool
|
||||
}{
|
||||
{
|
||||
name: "零值使用默认 MaxStreams",
|
||||
maxStreams: 0,
|
||||
idle: 0,
|
||||
enable0RTT: false,
|
||||
},
|
||||
{
|
||||
name: "自定义 MaxStreams",
|
||||
maxStreams: 500,
|
||||
idle: 60 * time.Second,
|
||||
enable0RTT: true,
|
||||
},
|
||||
{
|
||||
name: "最小 MaxStreams",
|
||||
maxStreams: 1,
|
||||
idle: 5 * time.Second,
|
||||
enable0RTT: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
MaxStreams: tt.maxStreams,
|
||||
IdleTimeout: tt.idle,
|
||||
Enable0RTT: tt.enable0RTT,
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
t.Cleanup(func() { _ = server.Stop() })
|
||||
|
||||
assert.True(t, server.running)
|
||||
assert.NotNil(t, server.listener)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestStart_MultipleStartsAndStops 测试多次启停
|
||||
func TestStart_MultipleStartsAndStops(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 第一次启动
|
||||
require.NoError(t, server.Start())
|
||||
assert.True(t, server.running)
|
||||
|
||||
// 停止
|
||||
require.NoError(t, server.Stop())
|
||||
assert.False(t, server.running)
|
||||
|
||||
// 第二次启动(使用新端口,因为旧端口可能还在释放中)
|
||||
server.config.Listen = ":0"
|
||||
require.NoError(t, server.Start())
|
||||
t.Cleanup(func() { _ = server.Stop() })
|
||||
assert.True(t, server.running)
|
||||
}
|
||||
|
||||
// TestStop_NotRunning 测试停止未运行的服务器
|
||||
func TestStop_NotRunning(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, server.running)
|
||||
|
||||
err = server.Stop()
|
||||
require.NoError(t, err)
|
||||
assert.False(t, server.running)
|
||||
}
|
||||
|
||||
// TestStop_Running 测试停止正在运行的服务器
|
||||
func TestStop_Running(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
assert.True(t, server.running)
|
||||
|
||||
require.NoError(t, server.Stop())
|
||||
assert.False(t, server.running)
|
||||
}
|
||||
|
||||
// TestStop_CalledMultipleTimes 测试多次停止不会报错
|
||||
func TestStop_CalledMultipleTimes(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
|
||||
require.NoError(t, server.Stop())
|
||||
require.NoError(t, server.Stop())
|
||||
require.NoError(t, server.Stop())
|
||||
|
||||
assert.False(t, server.running)
|
||||
}
|
||||
|
||||
// TestStartStop_Lifecycle 测试完整的生命周期
|
||||
func TestStartStop_Lifecycle(t *testing.T) {
|
||||
cfg := &config.HTTP3Config{
|
||||
Enabled: true,
|
||||
Listen: ":0",
|
||||
MaxStreams: 50,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
Enable0RTT: false,
|
||||
}
|
||||
handler := newTestHandler()
|
||||
tlsConfig := newTestTLSConfig(t)
|
||||
|
||||
server, err := NewServer(cfg, handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, server.running)
|
||||
assert.Nil(t, server.listener)
|
||||
assert.Nil(t, server.http3Server)
|
||||
|
||||
require.NoError(t, server.Start())
|
||||
|
||||
assert.True(t, server.running)
|
||||
assert.NotNil(t, server.listener)
|
||||
assert.NotNil(t, server.http3Server)
|
||||
|
||||
require.NoError(t, server.Stop())
|
||||
|
||||
assert.False(t, server.running)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user