996 lines
24 KiB
Go
996 lines
24 KiB
Go
// variable_test.go - 变量系统单元测试
|
||
//
|
||
// 测试覆盖:
|
||
// - 所有内置变量求值
|
||
// - 字符串展开($var 和 ${var} 格式)
|
||
// - 性能基准测试
|
||
//
|
||
// 作者:xfy
|
||
package variable
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
)
|
||
|
||
// mockRequestCtx 创建测试用的 fasthttp.RequestCtx
|
||
func mockRequestCtx(_ *testing.T) *fasthttp.RequestCtx {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
|
||
// 设置请求信息
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetRequestURI("/test/path?foo=bar&baz=qux")
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.Set("X-Custom-Header", "custom-value")
|
||
ctx.Request.Header.Set("User-Agent", "Test-Agent/1.0")
|
||
ctx.Request.Header.SetCookie("session", "abc123")
|
||
|
||
return ctx
|
||
}
|
||
|
||
// TestBuiltinVariables 测试所有内置变量
|
||
func TestBuiltinVariables(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
varName string
|
||
expected string
|
||
contains bool // 如果为 true,则检查是否包含
|
||
}{
|
||
{"host", VarHost, "example.com", false},
|
||
{"uri", VarURI, "/test/path", false},
|
||
{"request_uri", VarRequestURI, "/test/path?foo=bar&baz=qux", false},
|
||
{"args", VarArgs, "foo=bar&baz=qux", false},
|
||
{"request_method", VarRequestMethod, "GET", false},
|
||
{"scheme", VarScheme, "http", false},
|
||
{"time_iso8601", VarTimeISO8601, "", true}, // 包含格式特征
|
||
{"time_local", VarTimeLocal, "/", true}, // 包含 /
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
value, ok := vc.Get(tt.varName)
|
||
if !ok && !tt.contains {
|
||
t.Errorf("expected to find variable %s", tt.varName)
|
||
return
|
||
}
|
||
|
||
if tt.contains {
|
||
if !strings.Contains(value, tt.expected) {
|
||
t.Errorf("%s = %q, expected to contain %q", tt.varName, value, tt.expected)
|
||
}
|
||
} else {
|
||
if value != tt.expected {
|
||
t.Errorf("%s = %q, want %q", tt.varName, value, tt.expected)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestExpandSimple 测试简单变量展开
|
||
func TestExpandSimple(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"$host", "example.com"},
|
||
{"$uri", "/test/path"},
|
||
{"$request_method", "GET"},
|
||
{"$scheme", "http"},
|
||
{"Host: $host", "Host: example.com"},
|
||
{"$scheme://$host$uri", "http://example.com/test/path"},
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestExpandBrace 测试花括号变量展开
|
||
func TestExpandBrace(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"${host}", "example.com"},
|
||
{"${uri}", "/test/path"},
|
||
{"Host: ${host}", "Host: example.com"},
|
||
{"${scheme}://${host}${uri}", "http://example.com/test/path"},
|
||
{"${host}:8080", "example.com:8080"}, // 变量后有字符
|
||
{"pre_${uri}_post", "pre_/test/path_post"},
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestExpandMixed 测试混合格式展开
|
||
func TestExpandMixed(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"$scheme://${host}$uri", "http://example.com/test/path"},
|
||
{"${scheme}://$host${uri}", "http://example.com/test/path"},
|
||
{"$request_method ${request_uri} HTTP/1.1", "GET /test/path?foo=bar&baz=qux HTTP/1.1"},
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestExpandUndefined 测试未定义变量
|
||
func TestExpandUndefined(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"$undefined", "$undefined"}, // 保持原样
|
||
{"${undefined}", "${undefined}"}, // 保持原样
|
||
{"$host-$undefined", "example.com-$undefined"},
|
||
{"$host$undefined", "example.com$undefined"},
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestExpandEdgeCases 测试边界情况
|
||
func TestExpandEdgeCases(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"", ""}, // 空字符串
|
||
{"no variable", "no variable"}, // 无变量
|
||
{"$", "$"}, // 只有 $
|
||
{"${", "${"}, // 未闭合的 ${
|
||
{"$123", "$123"}, // 数字开头(不是有效变量名)
|
||
{"test$$host", "test$example.com"}, // 双 $
|
||
{"$host$$uri", "example.com$/test/path"},
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestCustomVariable 测试自定义变量
|
||
func TestCustomVariable(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置自定义变量
|
||
vc.Set("custom_var", "custom_value")
|
||
vc.Set("app_name", "lolly")
|
||
|
||
// 获取自定义变量
|
||
if v, ok := vc.Get("custom_var"); !ok || v != "custom_value" {
|
||
t.Errorf("custom_var = %q, want %q", v, "custom_value")
|
||
}
|
||
|
||
// 展开包含自定义变量
|
||
result := vc.Expand("App: $app_name")
|
||
if result != "App: lolly" {
|
||
t.Errorf("Expand = %q, want %q", result, "App: lolly")
|
||
}
|
||
}
|
||
|
||
// TestCustomOverridesBuiltin 测试自定义变量覆盖内置变量
|
||
func TestCustomOverridesBuiltin(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置同名自定义变量
|
||
vc.Set("host", "custom.host.com")
|
||
|
||
// 自定义变量应该覆盖内置变量
|
||
result := vc.Expand("$host")
|
||
if result != "custom.host.com" {
|
||
t.Errorf("Expand = %q, want %q", result, "custom.host.com")
|
||
}
|
||
}
|
||
|
||
// TestResponseInfoVariables 测试响应相关变量
|
||
func TestResponseInfoVariables(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置响应信息
|
||
vc.SetResponseInfo(200, 1024, 15000000) // 15ms
|
||
|
||
// 需要设置到 ctx 中才能被 builtin getter 获取
|
||
SetResponseInfoInContext(ctx, 200, 1024, 15000000)
|
||
|
||
tests := []struct {
|
||
varName string
|
||
expected string
|
||
}{
|
||
{VarStatus, "200"},
|
||
{VarBodyBytesSent, "1024"},
|
||
{VarRequestTime, "0.015"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.varName, func(t *testing.T) {
|
||
value, ok := vc.Get(tt.varName)
|
||
if !ok {
|
||
t.Errorf("expected to find variable %s", tt.varName)
|
||
return
|
||
}
|
||
if value != tt.expected {
|
||
t.Errorf("%s = %q, want %q", tt.varName, value, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// BenchmarkExpandSimple 基准测试:简单变量展开
|
||
func BenchmarkExpandSimple(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetRequestURI("/test")
|
||
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
template := "$host $request_method $uri"
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.Expand(template)
|
||
}
|
||
}
|
||
|
||
// BenchmarkExpandComplex 基准测试:复杂模板展开
|
||
func BenchmarkExpandComplex(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetRequestURI("/api/v1/users?page=1&limit=10")
|
||
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 模拟日志格式
|
||
template := "$remote_addr - - [$time_local] \"$request_method $request_uri HTTP/1.1\" $status $body_bytes_sent"
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.Expand(template)
|
||
}
|
||
}
|
||
|
||
// BenchmarkExpandNoVariable 基准测试:无变量字符串
|
||
func BenchmarkExpandNoVariable(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
template := "This is a static string without any variables"
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.Expand(template)
|
||
}
|
||
}
|
||
|
||
// BenchmarkExpandBrace 基准测试:花括号变量
|
||
func BenchmarkExpandBrace(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
template := "${scheme}://${host}${uri}"
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.Expand(template)
|
||
}
|
||
}
|
||
|
||
// BenchmarkPoolGetPut 基准测试:池的 Get/Put 性能
|
||
func BenchmarkPoolGetPut(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
vc := NewContext(ctx)
|
||
ReleaseContext(vc)
|
||
}
|
||
}
|
||
|
||
// TestPoolReuse 测试池复用
|
||
func TestPoolReuse(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
|
||
// 获取和释放多个 context,确保没有 panic
|
||
for i := range 10 {
|
||
vc := NewContext(ctx)
|
||
vc.Set("key", "value")
|
||
if v, ok := vc.Get("key"); !ok || v != "value" {
|
||
t.Errorf("iteration %d: expected key=value, got %s", i, v)
|
||
}
|
||
ReleaseContext(vc)
|
||
}
|
||
|
||
// 验证池在复用(第二次获取应该清除之前的值)
|
||
vc2 := NewContext(ctx)
|
||
if v, ok := vc2.Get("key"); ok {
|
||
t.Errorf("expected key to be cleared after release, got %s", v)
|
||
}
|
||
ReleaseContext(vc2)
|
||
}
|
||
|
||
// TestReleaseNilContext 测试释放 nil context
|
||
func TestReleaseNilContext(_ *testing.T) {
|
||
// 不应该 panic
|
||
ReleaseContext(nil)
|
||
}
|
||
|
||
// TestGetBuiltin 测试获取内置变量定义
|
||
func TestGetBuiltin(t *testing.T) {
|
||
// 存在的变量
|
||
v := GetBuiltin("host")
|
||
if v == nil || v.Name != "host" {
|
||
t.Error("GetBuiltin('host') should return non-nil with name 'host'")
|
||
}
|
||
|
||
// 不存在的变量
|
||
v = GetBuiltin("nonexistent")
|
||
if v != nil {
|
||
t.Error("GetBuiltin('nonexistent') should return nil")
|
||
}
|
||
}
|
||
|
||
// TestEmptyTemplate 测试空模板
|
||
func TestEmptyTemplate(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
result := vc.Expand("")
|
||
if result != "" {
|
||
t.Errorf("Expand('') = %q, want empty string", result)
|
||
}
|
||
}
|
||
|
||
// TestReleaseContextWithNil 测试释放 nil
|
||
func TestReleaseContextWithNil(_ *testing.T) {
|
||
// 不应该 panic
|
||
ReleaseContext(nil)
|
||
}
|
||
|
||
// TestExpandOnlyDollar 测试只有 $ 的情况
|
||
func TestExpandOnlyDollar(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
template string
|
||
expected string
|
||
}{
|
||
{"$", "$"},
|
||
{"test$", "test$"},
|
||
{"$$", "$$"}, // 两个独立的 $
|
||
}
|
||
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestPoolFunctions 测试 Pool 相关函数
|
||
func TestPoolFunctions(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
|
||
// 测试 NewContext 和 ReleaseContext
|
||
vc := NewContext(ctx)
|
||
if vc == nil {
|
||
t.Fatal("NewContext returned nil")
|
||
}
|
||
|
||
// 设置一些值
|
||
vc.Set("test", "value")
|
||
|
||
// 释放
|
||
ReleaseContext(vc)
|
||
|
||
// 再次获取应该被清空
|
||
vc2 := NewContext(ctx)
|
||
if _, ok := vc2.Get("test"); ok {
|
||
t.Error("expected context to be cleared after ReleaseContext")
|
||
}
|
||
ReleaseContext(vc2)
|
||
}
|
||
|
||
// TestPoolPutNil 测试 ReleaseContext nil
|
||
func TestPoolPutNil(_ *testing.T) {
|
||
// 不应该 panic
|
||
ReleaseContext(nil)
|
||
}
|
||
|
||
// TestSetResponseInfo 测试 SetResponseInfo
|
||
func TestSetResponseInfo(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置响应信息
|
||
vc.SetResponseInfo(404, 512, 25000000) // 25ms
|
||
|
||
if vc.status != 404 {
|
||
t.Errorf("status = %d, want 404", vc.status)
|
||
}
|
||
if vc.bodySize != 512 {
|
||
t.Errorf("bodySize = %d, want 512", vc.bodySize)
|
||
}
|
||
if vc.duration != 25000000 {
|
||
t.Errorf("duration = %d, want 25000000", vc.duration)
|
||
}
|
||
}
|
||
|
||
// TestSetServerName 测试 SetServerName
|
||
func TestSetServerName(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
vc.SetServerName("my-server")
|
||
if vc.serverName != "my-server" {
|
||
t.Errorf("serverName = %q, want 'my-server'", vc.serverName)
|
||
}
|
||
}
|
||
|
||
// TestUpstreamVariables 测试上游变量
|
||
func TestUpstreamVariables(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 未设置时应该返回默认值 "-"
|
||
tests := []struct {
|
||
varName string
|
||
expected string
|
||
}{
|
||
{VarUpstreamAddr, "-"},
|
||
{VarUpstreamStatus, "-"},
|
||
{VarUpstreamResponseTime, "-"},
|
||
{VarUpstreamConnectTime, "-"},
|
||
{VarUpstreamHeaderTime, "-"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.varName+"_default", func(t *testing.T) {
|
||
value, ok := vc.Get(tt.varName)
|
||
if !ok {
|
||
t.Errorf("expected variable %s to exist", tt.varName)
|
||
return
|
||
}
|
||
if value != tt.expected {
|
||
t.Errorf("%s = %q, want %q", tt.varName, value, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 设置上游变量
|
||
vc.SetUpstreamVars("http://backend:8080", 200, 0.123, 0.001, 0.045)
|
||
|
||
// 验证设置后的值
|
||
testsAfter := []struct {
|
||
varName string
|
||
expected string
|
||
}{
|
||
{VarUpstreamAddr, "http://backend:8080"},
|
||
{VarUpstreamStatus, "200"},
|
||
{VarUpstreamResponseTime, "0.123"},
|
||
{VarUpstreamConnectTime, "0.001"},
|
||
{VarUpstreamHeaderTime, "0.045"},
|
||
}
|
||
|
||
for _, tt := range testsAfter {
|
||
t.Run(tt.varName+"_set", func(t *testing.T) {
|
||
value, ok := vc.Get(tt.varName)
|
||
if !ok {
|
||
t.Errorf("expected variable %s to exist", tt.varName)
|
||
return
|
||
}
|
||
if value != tt.expected {
|
||
t.Errorf("%s = %q, want %q", tt.varName, value, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestUpstreamVariablesInExpand 测试在模板中展开上游变量
|
||
func TestUpstreamVariablesInExpand(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置上游变量
|
||
vc.SetUpstreamVars("http://backend:8080", 200, 0.123, 0.001, 0.045)
|
||
|
||
// 测试展开
|
||
template := "$upstream_addr $upstream_status $upstream_response_time"
|
||
result := vc.Expand(template)
|
||
expected := "http://backend:8080 200 0.123"
|
||
if result != expected {
|
||
t.Errorf("Expand = %q, want %q", result, expected)
|
||
}
|
||
}
|
||
|
||
// TestUpstreamVariablesErrorCases 测试上游变量错误情况
|
||
func TestUpstreamVariablesErrorCases(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 测试各种错误场景
|
||
tests := []struct {
|
||
expected map[string]string
|
||
name string
|
||
addr string
|
||
status int
|
||
}{
|
||
{
|
||
name: "no backend",
|
||
addr: "FAILED",
|
||
status: 502,
|
||
expected: map[string]string{
|
||
VarUpstreamAddr: "FAILED",
|
||
VarUpstreamStatus: "502",
|
||
},
|
||
},
|
||
{
|
||
name: "timeout",
|
||
addr: "http://backend:8080",
|
||
status: 504,
|
||
expected: map[string]string{
|
||
VarUpstreamAddr: "http://backend:8080",
|
||
VarUpstreamStatus: "504",
|
||
},
|
||
},
|
||
{
|
||
name: "cache hit",
|
||
addr: "CACHE",
|
||
status: 200,
|
||
expected: map[string]string{
|
||
VarUpstreamAddr: "CACHE",
|
||
VarUpstreamStatus: "200",
|
||
},
|
||
},
|
||
{
|
||
name: "websocket success",
|
||
addr: "ws://backend:8080",
|
||
status: 101,
|
||
expected: map[string]string{
|
||
VarUpstreamAddr: "ws://backend:8080",
|
||
VarUpstreamStatus: "101",
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
vc.SetUpstreamVars(tt.addr, tt.status, 0, 0, 0)
|
||
|
||
for varName, expected := range tt.expected {
|
||
value, ok := vc.Get(varName)
|
||
if !ok {
|
||
t.Errorf("expected variable %s to exist", varName)
|
||
continue
|
||
}
|
||
if value != expected {
|
||
t.Errorf("%s = %q, want %q", varName, value, expected)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestUpstreamVariablesZeroValues 测试上游变量零值处理
|
||
func TestUpstreamVariablesZeroValues(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 测试零值应该返回 "-"
|
||
vc.SetUpstreamVars("", 0, 0, 0, 0)
|
||
|
||
tests := []struct {
|
||
varName string
|
||
expected string
|
||
}{
|
||
{VarUpstreamAddr, "-"},
|
||
{VarUpstreamStatus, "-"},
|
||
{VarUpstreamResponseTime, "-"},
|
||
{VarUpstreamConnectTime, "-"},
|
||
{VarUpstreamHeaderTime, "-"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.varName, func(t *testing.T) {
|
||
value, ok := vc.Get(tt.varName)
|
||
if !ok {
|
||
t.Errorf("expected variable %s to exist", tt.varName)
|
||
return
|
||
}
|
||
if value != tt.expected {
|
||
t.Errorf("%s = %q, want %q", tt.varName, value, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// BenchmarkUpstreamVariables 基准测试:上游变量
|
||
func BenchmarkUpstreamVariables(b *testing.B) {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Request.Header.SetHost("example.com")
|
||
ctx.Request.Header.SetMethod("GET")
|
||
ctx.Request.Header.SetRequestURI("/test")
|
||
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置上游变量
|
||
vc.SetUpstreamVars("http://backend:8080", 200, 0.123, 0.001, 0.045)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_, _ = vc.Get(VarUpstreamAddr)
|
||
_, _ = vc.Get(VarUpstreamStatus)
|
||
_, _ = vc.Get(VarUpstreamResponseTime)
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGet 测试 EphemeralGet 方法
|
||
func TestEphemeralGet(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
name string
|
||
varName string
|
||
expected []byte
|
||
}{
|
||
{"host", VarHost, []byte("example.com")},
|
||
{"uri", VarURI, []byte("/test/path")},
|
||
{"request_uri", VarRequestURI, []byte("/test/path?foo=bar&baz=qux")},
|
||
{"args", VarArgs, []byte("foo=bar&baz=qux")},
|
||
{"request_method", VarRequestMethod, []byte("GET")},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := vc.EphemeralGet(tt.varName)
|
||
if string(result) != string(tt.expected) {
|
||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGetCustomVariable 测试自定义变量的 EphemeralGet
|
||
func TestEphemeralGetCustomVariable(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置自定义变量
|
||
vc.Set("custom_var", "custom_value")
|
||
|
||
result := vc.EphemeralGet("custom_var")
|
||
if string(result) != "custom_value" {
|
||
t.Errorf("EphemeralGet('custom_var') = %q, want 'custom_value'", result)
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGetUndefined 测试未定义变量的 EphemeralGet
|
||
func TestEphemeralGetUndefined(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
result := vc.EphemeralGet("undefined_var")
|
||
if result != nil {
|
||
t.Errorf("EphemeralGet('undefined_var') = %q, want nil", result)
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGetCache 测试 EphemeralGet 的缓存
|
||
func TestEphemeralGetCache(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 第一次获取
|
||
result1 := vc.EphemeralGet(VarHost)
|
||
// 第二次获取应该命中缓存
|
||
result2 := vc.EphemeralGet(VarHost)
|
||
|
||
// 验证缓存返回相同的结果
|
||
if string(result1) != string(result2) {
|
||
t.Errorf("EphemeralGet cache: %q != %q", result1, result2)
|
||
}
|
||
}
|
||
|
||
// TestPersistentGet 测试 PersistentGet 方法
|
||
func TestPersistentGet(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
tests := []struct {
|
||
name string
|
||
varName string
|
||
expected string
|
||
}{
|
||
{"host", VarHost, "example.com"},
|
||
{"uri", VarURI, "/test/path"},
|
||
{"request_method", VarRequestMethod, "GET"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := vc.PersistentGet(tt.varName)
|
||
if result != tt.expected {
|
||
t.Errorf("PersistentGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestPersistentGetUndefined 测试未定义变量的 PersistentGet
|
||
func TestPersistentGetUndefined(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
result := vc.PersistentGet("undefined_var")
|
||
if result != "" {
|
||
t.Errorf("PersistentGet('undefined_var') = %q, want empty string", result)
|
||
}
|
||
}
|
||
|
||
// TestGetBackwardCompatibility 测试 Get 方法的向后兼容性
|
||
func TestGetBackwardCompatibility(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// Get 应该返回与 PersistentGet 相同的结果
|
||
tests := []string{VarHost, VarURI, VarRequestMethod, VarRequestURI}
|
||
|
||
for _, varName := range tests {
|
||
getResult, ok := vc.Get(varName)
|
||
if !ok {
|
||
t.Errorf("Get(%q) returned not found", varName)
|
||
continue
|
||
}
|
||
persistentResult := vc.PersistentGet(varName)
|
||
if getResult != persistentResult {
|
||
t.Errorf("Get(%q) = %q, PersistentGet(%q) = %q, should be equal",
|
||
varName, getResult, varName, persistentResult)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGetUpstreamVariables 测试上游变量的 EphemeralGet
|
||
func TestEphemeralGetUpstreamVariables(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 未设置时应该返回默认值 "-"
|
||
defaultTests := []struct {
|
||
varName string
|
||
expected []byte
|
||
}{
|
||
{VarUpstreamAddr, []byte("-")},
|
||
{VarUpstreamStatus, []byte("-")},
|
||
{VarUpstreamResponseTime, []byte("-")},
|
||
}
|
||
|
||
for _, tt := range defaultTests {
|
||
t.Run(tt.varName+"_default", func(t *testing.T) {
|
||
result := vc.EphemeralGet(tt.varName)
|
||
if string(result) != string(tt.expected) {
|
||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 设置上游变量
|
||
vc.SetUpstreamVars("http://backend:8080", 200, 0.123, 0.001, 0.045)
|
||
|
||
setTests := []struct {
|
||
varName string
|
||
expected []byte
|
||
}{
|
||
{VarUpstreamAddr, []byte("http://backend:8080")},
|
||
{VarUpstreamStatus, []byte("200")},
|
||
{VarUpstreamResponseTime, []byte("0.123")},
|
||
}
|
||
|
||
for _, tt := range setTests {
|
||
t.Run(tt.varName+"_set", func(t *testing.T) {
|
||
result := vc.EphemeralGet(tt.varName)
|
||
if string(result) != string(tt.expected) {
|
||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestEphemeralGetResponseInfo 测试响应信息的 EphemeralGet
|
||
func TestEphemeralGetResponseInfo(t *testing.T) {
|
||
ctx := mockRequestCtx(t)
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 设置响应信息
|
||
vc.SetResponseInfo(200, 1024, 15000000) // 15ms
|
||
|
||
tests := []struct {
|
||
varName string
|
||
expected []byte
|
||
}{
|
||
{VarStatus, []byte("200")},
|
||
{VarBodyBytesSent, []byte("1024")},
|
||
{VarRequestTime, []byte("0.015")},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.varName, func(t *testing.T) {
|
||
result := vc.EphemeralGet(tt.varName)
|
||
if string(result) != string(tt.expected) {
|
||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// BenchmarkEphemeralGet 基准测试:EphemeralGet 零拷贝性能
|
||
func BenchmarkEphemeralGet(b *testing.B) {
|
||
ctx := setupBenchmarkRequestCtx()
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.EphemeralGet(VarHost)
|
||
}
|
||
}
|
||
|
||
// BenchmarkEphemeralGetCached 基准测试:EphemeralGet 缓存命中性能
|
||
func BenchmarkEphemeralGetCached(b *testing.B) {
|
||
ctx := setupBenchmarkRequestCtx()
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
// 预热缓存
|
||
_ = vc.EphemeralGet(VarHost)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.EphemeralGet(VarHost)
|
||
}
|
||
}
|
||
|
||
// BenchmarkPersistentGet 基准测试:PersistentGet 性能
|
||
func BenchmarkPersistentGet(b *testing.B) {
|
||
ctx := setupBenchmarkRequestCtx()
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
_ = vc.PersistentGet(VarHost)
|
||
}
|
||
}
|
||
|
||
// BenchmarkEphemeralGetMultiple 基准测试:获取多个变量的 EphemeralGet
|
||
func BenchmarkEphemeralGetMultiple(b *testing.B) {
|
||
ctx := setupBenchmarkRequestCtx()
|
||
vc := NewContext(ctx)
|
||
defer ReleaseContext(vc)
|
||
|
||
vars := []string{VarHost, VarURI, VarRequestMethod, VarRequestURI, VarArgs}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
for _, name := range vars {
|
||
_ = vc.EphemeralGet(name)
|
||
}
|
||
}
|
||
}
|