// Package server 提供 startSingleMode 集成测试。 // // 该文件测试 startSingleMode 函数的各种配置场景, // 包括静态文件、代理、监控端点、TLS 等配置的实际启动。 // // 作者:xfy package server import ( "os" "strings" "testing" "time" "rua.plus/lolly/internal/config" ) // TestStartSingleMode_Integration_WithStaticFiles 测试 startSingleMode 静态文件实际启动。 func TestStartSingleMode_Integration_WithStaticFiles(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } tempDir := t.TempDir() // 创建一个测试文件 testFile := tempDir + "/index.html" if err := os.WriteFile(testFile, []byte("test"), 0o644); err != nil { t.Fatalf("failed to create test file: %v", err) } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Static: []config.StaticConfig{ { Path: "/static", Root: tempDir, Index: []string{"index.html"}, }, }, }}, } s := New(cfg) // 在 goroutine 中启动服务器 errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() // 等待服务器启动 waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: // 服务器正常关闭会返回 nil 或 listener closed 错误 if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: // 服务器仍在运行,已通过 GracefulStop 关闭 } } // TestStartSingleMode_Integration_WithProxy 测试 startSingleMode 代理实际启动。 func TestStartSingleMode_Integration_WithProxy(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Proxy: []config.ProxyConfig{ { Path: "/api", Targets: []config.ProxyTarget{ {URL: "http://127.0.0.1:9999", Weight: 1}, // 不存在的后端 }, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithMonitoring 测试 startSingleMode 监控端点实际启动。 func TestStartSingleMode_Integration_WithMonitoring(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", }}, Monitoring: config.MonitoringConfig{ Status: config.StatusConfig{ Enabled: true, Path: "/_status", Allow: []string{"127.0.0.1"}, }, Pprof: config.PprofConfig{ Enabled: true, Path: "/debug/pprof", }, }, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithCacheAPI 测试 startSingleMode 缓存 API 实际启动。 func TestStartSingleMode_Integration_WithCacheAPI(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", CacheAPI: &config.CacheAPIConfig{ Enabled: true, Path: "/_cache/purge", }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithCompression 测试 startSingleMode 压缩配置实际启动。 func TestStartSingleMode_Integration_WithCompression(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Compression: config.CompressionConfig{ Type: "gzip", Level: 6, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithSecurity 测试 startSingleMode 安全配置实际启动。 func TestStartSingleMode_Integration_WithSecurity(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Security: config.SecurityConfig{ Access: config.AccessConfig{ Allow: []string{"127.0.0.1"}, }, RateLimit: config.RateLimitConfig{ RequestRate: 100, Burst: 200, }, Headers: config.SecurityHeaders{ XFrameOptions: "DENY", }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithRewrite 测试 startSingleMode 重写配置实际启动。 func TestStartSingleMode_Integration_WithRewrite(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Rewrite: []config.RewriteRule{ {Pattern: "^/old/(.*)$", Replacement: "/new/$1"}, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithPerformance 测试 startSingleMode 性能配置实际启动。 func TestStartSingleMode_Integration_WithPerformance(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", }}, Performance: config.PerformanceConfig{ GoroutinePool: config.GoroutinePoolConfig{ Enabled: true, MaxWorkers: 50, MinWorkers: 5, IdleTimeout: 10 * time.Second, }, FileCache: config.FileCacheConfig{ MaxEntries: 1000, MaxSize: 10 * 1024 * 1024, }, }, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithProxyLocationTypes 测试代理不同位置类型实际启动。 func TestStartSingleMode_Integration_WithProxyLocationTypes(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Proxy: []config.ProxyConfig{ { Path: "/api/exact", LocationType: "exact", Targets: []config.ProxyTarget{{URL: "http://127.0.0.1:9999", Weight: 1}}, }, { Path: "/api/priority", LocationType: "prefix_priority", Targets: []config.ProxyTarget{{URL: "http://127.0.0.1:9999", Weight: 1}}, }, { Path: "^/api/regex/(.*)$", LocationType: "regex", Targets: []config.ProxyTarget{{URL: "http://127.0.0.1:9999", Weight: 1}}, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithStaticLocationTypes 测试静态文件不同位置类型实际启动。 func TestStartSingleMode_Integration_WithStaticLocationTypes(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } tempDir := t.TempDir() cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Static: []config.StaticConfig{ { Path: "/static/exact", Root: tempDir, LocationType: "exact", }, { Path: "/static/priority", Root: tempDir, LocationType: "prefix_priority", }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithHealthCheck 测试代理健康检查实际启动。 func TestStartSingleMode_Integration_WithHealthCheck(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Proxy: []config.ProxyConfig{ { Path: "/api", Targets: []config.ProxyTarget{ {URL: "http://127.0.0.1:9999", Weight: 1}, }, HealthCheck: config.HealthCheckConfig{ Interval: 1 * time.Second, Timeout: 500 * time.Millisecond, Path: "/health", }, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithMIMETypes 测试 MIME 类型配置实际启动。 func TestStartSingleMode_Integration_WithMIMETypes(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Types: config.TypesConfig{ Map: map[string]string{ ".wasm": "application/wasm", }, DefaultType: "application/octet-stream", }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithErrorPage 测试错误页面配置实际启动。 func TestStartSingleMode_Integration_WithErrorPage(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } tempDir := t.TempDir() errorPage := tempDir + "/404.html" if err := os.WriteFile(errorPage, []byte("Not Found"), 0o644); err != nil { t.Fatalf("failed to create error page: %v", err) } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Security: config.SecurityConfig{ ErrorPage: config.ErrorPageConfig{ Pages: map[int]string{404: errorPage}, Default: errorPage, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithConnLimiter 测试连接限制配置实际启动。 func TestStartSingleMode_Integration_WithConnLimiter(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Security: config.SecurityConfig{ RateLimit: config.RateLimitConfig{ ConnLimit: 10, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // TestStartSingleMode_Integration_WithAuthRequest 测试外部认证配置实际启动。 func TestStartSingleMode_Integration_WithAuthRequest(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } cfg := &config.Config{ Servers: []config.ServerConfig{{ Listen: "127.0.0.1:0", Security: config.SecurityConfig{ AuthRequest: config.AuthRequestConfig{ Enabled: true, URI: "/auth/validate", Timeout: 5 * time.Second, }, }, }}, } s := New(cfg) errCh := make(chan error, 1) go func() { if err := s.Start(); err != nil { errCh <- err } }() waitForServerRunning(s, 2*time.Second) _ = s.GracefulStop(2 * time.Second) select { case err := <-errCh: if err != nil && !isExpectedServerErrorForIntegration(err) { t.Errorf("unexpected server error: %v", err) } default: } } // isExpectedServerErrorForIntegration 检查是否是预期的服务器关闭错误。 func isExpectedServerErrorForIntegration(err error) bool { if err == nil { return true } // fasthttp 服务器关闭时的常见错误 errStr := err.Error() return strings.Contains(errStr, "closed") || strings.Contains(errStr, "use of closed") || strings.Contains(errStr, "listener closed") }