添加配置生成、常量定义、测试设置、SSL 和 WebSocket 工具函数。 重构 container.go 支持函数式选项模式配置容器。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
210 lines
4.6 KiB
Go
210 lines
4.6 KiB
Go
//go:build e2e
|
||
|
||
// Package testutil 提供 E2E 测试的工具函数。
|
||
//
|
||
// 包含统一的测试环境设置函数。
|
||
//
|
||
// 作者:xfy
|
||
package testutil
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// E2ETestEnv E2E 测试环境。
|
||
//
|
||
// 封装测试所需的资源和清理函数。
|
||
type E2ETestEnv struct {
|
||
Ctx context.Context
|
||
Network string
|
||
Pool *BackendPool
|
||
Lolly *LollyContainer
|
||
Client *http.Client
|
||
cleanup func()
|
||
}
|
||
|
||
// SetupE2ETest 设置 E2E 测试环境。
|
||
//
|
||
// 自动处理镜像检查、后端启动、lolly 启动和资源清理。
|
||
// 使用 t.Cleanup() 确保资源正确释放。
|
||
//
|
||
// 参数:
|
||
// - t: 测试对象
|
||
// - backendCount: 后端数量
|
||
// - cfgBuilder: 配置构建函数,接收后端池返回 YAML 配置
|
||
//
|
||
// 使用示例:
|
||
//
|
||
// env := testutil.SetupE2ETest(t, 2, func(pool *testutil.BackendPool) string {
|
||
// cfg := testutil.NewConfigBuilder().
|
||
// WithServer(":8080").
|
||
// WithProxy("/", pool.InternalAddresses())
|
||
// yaml, _ := cfg.Build()
|
||
// return yaml
|
||
// })
|
||
// defer env.Cleanup()
|
||
func SetupE2ETest(t *testing.T, backendCount int, cfgBuilder func(*BackendPool) string) *E2ETestEnv {
|
||
t.Helper()
|
||
|
||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTestTimeout)
|
||
|
||
if !LollyImageAvailable(ctx) {
|
||
t.Skip("lolly:latest image not available, run 'make docker-build' first")
|
||
}
|
||
|
||
network, pool, err := SetupProxyTest(ctx, backendCount)
|
||
if err != nil {
|
||
cancel()
|
||
t.Fatalf("Failed to setup proxy test: %v", err)
|
||
}
|
||
|
||
cfgYAML := cfgBuilder(pool)
|
||
|
||
lolly, err := StartLolly(ctx,
|
||
WithConfigYAML(cfgYAML),
|
||
WithNetwork(network),
|
||
)
|
||
if err != nil {
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
t.Fatalf("Failed to start lolly: %v", err)
|
||
}
|
||
|
||
if err := lolly.WaitForHealthy(ctx, HealthCheckWaitTimeout); err != nil {
|
||
lolly.Terminate(ctx)
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
t.Fatalf("Lolly not healthy: %v", err)
|
||
}
|
||
|
||
env := &E2ETestEnv{
|
||
Ctx: ctx,
|
||
Network: network,
|
||
Pool: pool,
|
||
Lolly: lolly,
|
||
Client: CreateDefaultHTTPClient(),
|
||
}
|
||
|
||
env.cleanup = func() {
|
||
lolly.Terminate(ctx)
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
}
|
||
|
||
t.Cleanup(env.Cleanup)
|
||
|
||
return env
|
||
}
|
||
|
||
// SetupE2ETestWithTimeout 设置带自定义超时的 E2E 测试环境。
|
||
func SetupE2ETestWithTimeout(t *testing.T, backendCount int, timeout time.Duration, cfgBuilder func(*BackendPool) string) *E2ETestEnv {
|
||
t.Helper()
|
||
|
||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||
|
||
if !LollyImageAvailable(ctx) {
|
||
t.Skip("lolly:latest image not available, run 'make docker-build' first")
|
||
}
|
||
|
||
network, pool, err := SetupProxyTest(ctx, backendCount)
|
||
if err != nil {
|
||
cancel()
|
||
t.Fatalf("Failed to setup proxy test: %v", err)
|
||
}
|
||
|
||
cfgYAML := cfgBuilder(pool)
|
||
|
||
lolly, err := StartLolly(ctx,
|
||
WithConfigYAML(cfgYAML),
|
||
WithNetwork(network),
|
||
)
|
||
if err != nil {
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
t.Fatalf("Failed to start lolly: %v", err)
|
||
}
|
||
|
||
if err := lolly.WaitForHealthy(ctx, HealthCheckWaitTimeout); err != nil {
|
||
lolly.Terminate(ctx)
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
t.Fatalf("Lolly not healthy: %v", err)
|
||
}
|
||
|
||
env := &E2ETestEnv{
|
||
Ctx: ctx,
|
||
Network: network,
|
||
Pool: pool,
|
||
Lolly: lolly,
|
||
Client: CreateDefaultHTTPClient(),
|
||
}
|
||
|
||
env.cleanup = func() {
|
||
lolly.Terminate(ctx)
|
||
CleanupProxyTest(ctx, network, pool)
|
||
cancel()
|
||
}
|
||
|
||
t.Cleanup(env.Cleanup)
|
||
|
||
return env
|
||
}
|
||
|
||
// Cleanup 手动清理资源。
|
||
func (e *E2ETestEnv) Cleanup() {
|
||
if e.cleanup != nil {
|
||
e.cleanup()
|
||
e.cleanup = nil
|
||
}
|
||
}
|
||
|
||
// HTTPURL 返回 lolly HTTP 地址。
|
||
func (e *E2ETestEnv) HTTPURL() string {
|
||
return e.Lolly.HTTPBaseURL()
|
||
}
|
||
|
||
// HTTPSURL 返回 lolly HTTPS 地址。
|
||
func (e *E2ETestEnv) HTTPSURL() string {
|
||
return e.Lolly.HTTPSBaseURL()
|
||
}
|
||
|
||
// SetupSSLTest 设置 SSL 测试环境。
|
||
//
|
||
// 用于 SSL/TLS 测试场景,自动生成证书。
|
||
func SetupSSLTest(t *testing.T, cfgBuilder func() string) (*LollyContainer, string, string) {
|
||
t.Helper()
|
||
|
||
ctx := context.Background()
|
||
|
||
if !LollyImageAvailable(ctx) {
|
||
t.Skip("lolly:latest image not available, run 'make docker-build' first")
|
||
}
|
||
|
||
// 生成自签名证书
|
||
certPath, keyPath, cleanup, err := GenerateSelfSignedCert(t.TempDir())
|
||
if err != nil {
|
||
t.Fatalf("Failed to generate certificate: %v", err)
|
||
}
|
||
t.Cleanup(cleanup)
|
||
|
||
cfgYAML := cfgBuilder()
|
||
|
||
lolly, err := StartLolly(ctx,
|
||
WithConfigYAML(cfgYAML),
|
||
WithCert(certPath, keyPath),
|
||
)
|
||
if err != nil {
|
||
t.Fatalf("Failed to start lolly: %v", err)
|
||
}
|
||
t.Cleanup(func() { lolly.Terminate(ctx) })
|
||
|
||
if err := lolly.WaitForHealthy(ctx, HealthCheckWaitTimeout); err != nil {
|
||
t.Fatalf("Lolly not healthy: %v", err)
|
||
}
|
||
|
||
return lolly, certPath, keyPath
|
||
}
|