lolly/internal/lua/api_var_test.go
xfy 51061d68ff feat(lua): 实现 ngx.var API
通过元表实现动态变量读写,支持 nginx 内置变量和自定义变量

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 12:17:35 +08:00

285 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}