Apply modern Go patterns across the codebase:
- Replace `interface{}` with `any` (Go 1.18+)
- Use `for range n` instead of `for i := 0; i < n; i++` (Go 1.22+)
- Replace `sort.Slice` with `slices.Sort` from slices package
- Simplify sync.WaitGroup patterns with errgroup where appropriate
- Add Makefile targets for modernize analyzer
Total: 84 files updated, net reduction of 79 lines
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
233 lines
6.6 KiB
Go
233 lines
6.6 KiB
Go
// integration_test.go - 变量系统集成测试
|
||
//
|
||
// 测试变量系统与 logging、proxy、rewrite 的集成
|
||
//
|
||
// 作者:xfy
|
||
package variable_test
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
"rua.plus/lolly/internal/config"
|
||
"rua.plus/lolly/internal/logging"
|
||
"rua.plus/lolly/internal/middleware/rewrite"
|
||
"rua.plus/lolly/internal/variable"
|
||
)
|
||
|
||
// TestVariableInAccessLog 测试访问日志中的变量展开
|
||
func TestVariableInAccessLog(_ *testing.T) {
|
||
// 创建测试请求上下文
|
||
cfg := &config.LoggingConfig{
|
||
Access: config.AccessLogConfig{
|
||
Format: "$remote_addr - $remote_user [$time_local] \"$request_method $uri $scheme\" $status $body_bytes_sent",
|
||
},
|
||
}
|
||
|
||
logger := logging.New(cfg)
|
||
|
||
// 创建请求上下文
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/api/users?page=1")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetHost("example.com")
|
||
|
||
// 记录访问日志
|
||
logger.LogAccess(ctx, 200, 1024, 50*time.Millisecond)
|
||
|
||
// 验证输出包含期望的变量
|
||
// 注意:由于直接输出到文件/stdout,这里主要验证不 panic
|
||
}
|
||
|
||
// TestVariableInRewrite 测试重写规则中的变量展开
|
||
func TestVariableInRewrite(t *testing.T) {
|
||
rules := []config.RewriteRule{
|
||
{
|
||
Pattern: "^/api/(.*)$",
|
||
Replacement: "/v1/$1?original=$uri",
|
||
Flag: "break",
|
||
},
|
||
}
|
||
|
||
mw, err := rewrite.New(rules)
|
||
if err != nil {
|
||
t.Fatalf("failed to create rewrite middleware: %v", err)
|
||
}
|
||
|
||
// 创建请求
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/api/users")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetHost("example.com")
|
||
|
||
// 创建处理函数来捕获重写后的路径
|
||
var capturedPath string
|
||
next := func(c *fasthttp.RequestCtx) {
|
||
capturedPath = string(c.Path())
|
||
}
|
||
|
||
// 处理请求
|
||
handler := mw.Process(next)
|
||
handler(ctx)
|
||
|
||
// 验证路径被重写
|
||
if capturedPath != "/v1/users" {
|
||
t.Errorf("expected path '/v1/users', got %q", capturedPath)
|
||
}
|
||
}
|
||
|
||
// TestVariableCompatibility 测试与旧格式的兼容性
|
||
func TestVariableCompatibility(t *testing.T) {
|
||
// 测试旧格式变量名
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/test?foo=bar")
|
||
ctx.Request.Header.SetMethod("POST")
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.Set("User-Agent", "TestAgent")
|
||
ctx.Request.Header.Set("Referer", "http://referer.com")
|
||
|
||
// 设置响应信息(模拟日志场景)
|
||
variable.SetResponseInfoInContext(ctx, 201, 2048, 100000000) // 100ms
|
||
|
||
vc := variable.NewContext(ctx)
|
||
defer variable.ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
contains []string // 验证结果包含这些子串
|
||
}{
|
||
{"$remote_addr", []string{"0.0.0.0"}}, // 默认地址
|
||
{"$host", []string{"example.com"}},
|
||
{"$uri", []string{"/test"}},
|
||
{"$request_method", []string{"POST"}},
|
||
{"$scheme", []string{"http"}},
|
||
{"$status", []string{"201"}},
|
||
{"$body_bytes_sent", []string{"2048"}},
|
||
{"$request_time", []string{"0.100"}},
|
||
{"$time_local", []string{"/"}}, // 包含 /
|
||
{"$time_iso8601", []string{"-"}}, // ISO8601 包含 -
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.template, func(t *testing.T) {
|
||
result := vc.Expand(tt.template)
|
||
for _, expected := range tt.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("Expand(%q) = %q, expected to contain %q", tt.template, result, expected)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestVariableExpansionPerformance 测试变量展开性能
|
||
func TestVariableExpansionPerformance(t *testing.T) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/api/v1/users/123?active=true")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetHost("api.example.com")
|
||
|
||
vc := variable.NewContext(ctx)
|
||
defer variable.ReleaseContext(vc)
|
||
|
||
// 常见日志格式模板
|
||
template := "$remote_addr - $remote_user [$time_local] \"$request_method $request_uri $scheme\" $status $body_bytes_sent \"$http_user_agent\""
|
||
|
||
// 执行多次展开
|
||
start := time.Now()
|
||
iterations := 10000
|
||
for range iterations {
|
||
_ = vc.Expand(template)
|
||
}
|
||
elapsed := time.Since(start)
|
||
|
||
// 计算平均时间
|
||
avg := elapsed / time.Duration(iterations)
|
||
t.Logf("Average expansion time: %v (iterations: %d)", avg, iterations)
|
||
|
||
// 验证性能在合理范围内(< 1μs 每次)
|
||
if avg > time.Microsecond {
|
||
t.Logf("Warning: average time %v exceeds 1μs", avg)
|
||
}
|
||
}
|
||
|
||
// TestMixedVariableFormats 测试混合变量格式
|
||
func TestMixedVariableFormats(t *testing.T) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/test")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetHost("example.com")
|
||
|
||
vc := variable.NewContext(ctx)
|
||
defer variable.ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"$scheme://$host$uri", "http://example.com/test"},
|
||
{"${scheme}://${host}${uri}", "http://example.com/test"},
|
||
{"Host: ${host}:8080", "Host: example.com:8080"},
|
||
{"$host:8080", "example.com:8080"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.template, func(t *testing.T) {
|
||
result := vc.Expand(tt.template)
|
||
if result != tt.expected {
|
||
t.Errorf("Expand(%q) = %q, want %q", tt.template, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestUndefinedVariableInIntegration 测试未定义变量在集成场景中的行为
|
||
func TestUndefinedVariableInIntegration(t *testing.T) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.SetRequestURI("/test")
|
||
|
||
vc := variable.NewContext(ctx)
|
||
defer variable.ReleaseContext(vc)
|
||
|
||
// 未定义变量应该保持原样
|
||
template := "$host $undefined_var $uri"
|
||
result := vc.Expand(template)
|
||
|
||
// $host 和 $uri 应该展开,$undefined_var 保持原样
|
||
if !strings.Contains(result, "example.com") && !strings.Contains(result, "$undefined_var") {
|
||
t.Errorf("expected result to contain either expanded host or $undefined_var, got %q", result)
|
||
}
|
||
}
|
||
|
||
// TestVariableContextReuse 测试变量上下文复用
|
||
func TestVariableContextReuse(t *testing.T) {
|
||
// 创建两个请求
|
||
ctx1 := &fasthttp.RequestCtx{}
|
||
ctx1.Request.SetRequestURI("/first")
|
||
ctx1.Request.Header.SetHost("first.com")
|
||
|
||
ctx2 := &fasthttp.RequestCtx{}
|
||
ctx2.Request.SetRequestURI("/second")
|
||
ctx2.Request.Header.SetHost("second.com")
|
||
|
||
// 使用第一个上下文
|
||
vc1 := variable.NewContext(ctx1)
|
||
result1 := vc1.Expand("$host$uri")
|
||
variable.ReleaseContext(vc1)
|
||
|
||
// 复用(从池中获取)用于第二个上下文
|
||
vc2 := variable.NewContext(ctx2)
|
||
result2 := vc2.Expand("$host$uri")
|
||
variable.ReleaseContext(vc2)
|
||
|
||
// 验证结果正确
|
||
if result1 != "first.com/first" {
|
||
t.Errorf("first request: expected 'first.com/first', got %q", result1)
|
||
}
|
||
if result2 != "second.com/second" {
|
||
t.Errorf("second request: expected 'second.com/second', got %q", result2)
|
||
}
|
||
}
|