通过元表实现动态变量读写,支持 nginx 内置变量和自定义变量 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
285 lines
6.9 KiB
Go
285 lines
6.9 KiB
Go
// Package lua 提供 ngx.var API 测试
|
||
package lua
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
"github.com/valyala/fasthttp"
|
||
)
|
||
|
||
// TestNgxVarAPI 测试 ngx.var API 基础功能
|
||
func TestNgxVarAPI(t *testing.T) {
|
||
// 创建 fasthttp 请求上下文
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("GET")
|
||
req.Header.SetRequestURI("/test/path?foo=bar&baz=qux")
|
||
req.Header.Set("Host", "example.com")
|
||
req.Header.Set("User-Agent", "TestAgent")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
// 创建协程
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
// 设置沙箱(这会注册 ngx API)
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试 ngx.var 存在
|
||
err = coro.Execute(`
|
||
if type(ngx) ~= "table" then
|
||
error("ngx is not a table")
|
||
end
|
||
if type(ngx.var) ~= "table" then
|
||
error("ngx.var is not a table")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarReadBuiltin 测试读取内置变量
|
||
func TestNgxVarReadBuiltin(t *testing.T) {
|
||
// 创建 fasthttp 请求上下文
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("POST")
|
||
req.Header.SetRequestURI("/api/test?name=value")
|
||
req.Header.Set("Host", "test.example.com")
|
||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||
req.Header.Set("Accept", "application/json")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试读取内置变量
|
||
err = coro.Execute(`
|
||
local method = ngx.var.request_method
|
||
if method ~= "POST" then
|
||
error("request_method should be POST, got: " .. tostring(method))
|
||
end
|
||
|
||
local uri = ngx.var.uri
|
||
if uri ~= "/api/test" then
|
||
error("uri should be /api/test, got: " .. tostring(uri))
|
||
end
|
||
|
||
local host = ngx.var.http_host
|
||
if host ~= "test.example.com" then
|
||
error("http_host should be test.example.com, got: " .. tostring(host))
|
||
end
|
||
|
||
local userAgent = ngx.var.http_user_agent
|
||
if userAgent ~= "Mozilla/5.0" then
|
||
error("http_user_agent should be Mozilla/5.0, got: " .. tostring(userAgent))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarReadQueryArgs 测试读取查询参数
|
||
func TestNgxVarReadQueryArgs(t *testing.T) {
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("GET")
|
||
req.Header.SetRequestURI("/search?q=lua&page=1")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试读取查询参数
|
||
err = coro.Execute(`
|
||
local q = ngx.var.arg_q
|
||
if q ~= "lua" then
|
||
error("arg_q should be 'lua', got: " .. tostring(q))
|
||
end
|
||
|
||
local page = ngx.var.arg_page
|
||
if page ~= "1" then
|
||
error("arg_page should be '1', got: " .. tostring(page))
|
||
end
|
||
|
||
local queryString = ngx.var.query_string
|
||
if type(queryString) ~= "string" then
|
||
error("query_string should be a string")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarWriteCustom 测试设置自定义变量
|
||
func TestNgxVarWriteCustom(t *testing.T) {
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("GET")
|
||
req.Header.SetRequestURI("/test")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试设置和读取自定义变量
|
||
err = coro.Execute(`
|
||
-- 设置自定义变量
|
||
ngx.var.my_custom_var = "hello world"
|
||
ngx.var.another_var = "12345"
|
||
|
||
-- 读取自定义变量
|
||
local val1 = ngx.var.my_custom_var
|
||
if val1 ~= "hello world" then
|
||
error("my_custom_var should be 'hello world', got: " .. tostring(val1))
|
||
end
|
||
|
||
local val2 = ngx.var.another_var
|
||
if val2 ~= "12345" then
|
||
error("another_var should be '12345', got: " .. tostring(val2))
|
||
end
|
||
|
||
-- 覆盖已存在的变量
|
||
ngx.var.my_custom_var = "updated"
|
||
local val3 = ngx.var.my_custom_var
|
||
if val3 ~= "updated" then
|
||
error("my_custom_var should be 'updated', got: " .. tostring(val3))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarIndexAccess 测试索引访问方式 ngx.var[key]
|
||
func TestNgxVarIndexAccess(t *testing.T) {
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("GET")
|
||
req.Header.SetRequestURI("/test")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试索引访问
|
||
err = coro.Execute(`
|
||
-- 使用索引方式设置变量
|
||
ngx.var["dynamic_key"] = "dynamic_value"
|
||
|
||
-- 使用索引方式读取变量
|
||
local val = ngx.var["dynamic_key"]
|
||
if val ~= "dynamic_value" then
|
||
error("dynamic_key should be 'dynamic_value', got: " .. tostring(val))
|
||
end
|
||
|
||
-- 混合访问方式
|
||
ngx.var.mixed = "mixed_value"
|
||
local mixed = ngx.var["mixed"]
|
||
if mixed ~= "mixed_value" then
|
||
error("mixed should be 'mixed_value', got: " .. tostring(mixed))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarNilRequestCtx 测试无请求上下文的情况
|
||
func TestNgxVarNilRequestCtx(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
// 创建没有请求上下文的协程
|
||
coro, err := engine.NewCoroutine(nil)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试在无请求上下文时访问 ngx.var
|
||
err = coro.Execute(`
|
||
-- 应该返回空字符串或 nil
|
||
local method = ngx.var.request_method
|
||
-- 可以接受空字符串或 nil
|
||
|
||
-- 但自定义变量仍然可以设置
|
||
ngx.var.test = "value"
|
||
local val = ngx.var.test
|
||
if val ~= "value" then
|
||
error("custom var should be settable even without request ctx")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestNgxVarUndefined 测试未定义变量
|
||
func TestNgxVarUndefined(t *testing.T) {
|
||
var req fasthttp.Request
|
||
req.Header.SetMethod("GET")
|
||
req.Header.SetRequestURI("/test")
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&req, nil, nil)
|
||
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
coro, err := engine.NewCoroutine(ctx)
|
||
require.NoError(t, err)
|
||
defer coro.Close()
|
||
|
||
err = coro.SetupSandbox()
|
||
require.NoError(t, err)
|
||
|
||
// 测试读取未定义变量
|
||
err = coro.Execute(`
|
||
local undefined = ngx.var.undefined_var_name
|
||
if undefined ~= nil then
|
||
error("undefined var should be nil, got: " .. tostring(undefined))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|