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>
484 lines
11 KiB
Go
484 lines
11 KiB
Go
// Package lua 提供 Scheduler 模式测试。
|
||
//
|
||
// 该文件测试 Lua Scheduler 模式的功能:
|
||
// - setupSchedulerNgxAPI
|
||
// - setSchedulerMode / IsSchedulerMode
|
||
// - SchedulerUnsafeLocationAPI
|
||
// - SchedulerUnsafeVarAPI
|
||
// - SchedulerUnsafeCtxAPI
|
||
//
|
||
// 作者:xfy
|
||
package lua
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
glua "github.com/yuin/gopher-lua"
|
||
)
|
||
|
||
// TestSchedulerModeFlag 测试 Scheduler 模式标志。
|
||
func TestSchedulerModeFlag(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
// 初始状态不是 scheduler 模式
|
||
if IsSchedulerMode(L) {
|
||
t.Error("Initial state should not be scheduler mode")
|
||
}
|
||
|
||
// 设置 scheduler 模式
|
||
setSchedulerMode(L, true)
|
||
if !IsSchedulerMode(L) {
|
||
t.Error("Should be in scheduler mode after setting")
|
||
}
|
||
|
||
// 关闭 scheduler 模式
|
||
setSchedulerMode(L, false)
|
||
if IsSchedulerMode(L) {
|
||
t.Error("Should not be in scheduler mode after unsetting")
|
||
}
|
||
}
|
||
|
||
// TestSetupSchedulerNgxAPI 测试 setupSchedulerNgxAPI 创建安全的 ngx API。
|
||
func TestSetupSchedulerNgxAPI(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
// 调用 setupSchedulerNgxAPI
|
||
setupSchedulerNgxAPI(L, engine)
|
||
|
||
// 验证 ngx 表存在
|
||
ngx := L.GetGlobal("ngx")
|
||
if ngx == glua.LNil {
|
||
t.Fatal("ngx table should exist")
|
||
}
|
||
|
||
ngxTable, ok := ngx.(*glua.LTable)
|
||
if !ok {
|
||
t.Fatal("ngx should be a table")
|
||
}
|
||
|
||
// 验证 scheduler 模式已设置
|
||
if !IsSchedulerMode(L) {
|
||
t.Error("Should be in scheduler mode after setupSchedulerNgxAPI")
|
||
}
|
||
|
||
// 验证 ngx.shared 存在
|
||
shared := ngxTable.RawGetString("shared")
|
||
if shared == glua.LNil {
|
||
t.Error("ngx.shared should exist")
|
||
}
|
||
|
||
// 验证 ngx.log 存在
|
||
log := ngxTable.RawGetString("log")
|
||
if log == glua.LNil {
|
||
t.Error("ngx.log should exist")
|
||
}
|
||
|
||
// 验证 ngx.timer 存在
|
||
timer := ngxTable.RawGetString("timer")
|
||
if timer == glua.LNil {
|
||
t.Error("ngx.timer should exist")
|
||
}
|
||
}
|
||
|
||
// TestSchedulerUnsafeLocationAPI 测试 Scheduler 模式下 ngx.location 不可用。
|
||
func TestSchedulerUnsafeLocationAPI(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册不安全的 location API
|
||
RegisterSchedulerUnsafeLocationAPI(L, ngx)
|
||
|
||
// 验证 ngx.location 存在
|
||
location := ngx.RawGetString("location")
|
||
if location == glua.LNil {
|
||
t.Fatal("ngx.location should exist")
|
||
}
|
||
|
||
// 尝试调用 ngx.location.capture 应该返回错误
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx.location.capture("/test")
|
||
end)
|
||
if ok then
|
||
error("ngx.location.capture should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerUnsafeVarAPI 测试 Scheduler 模式下 ngx.var 不可用。
|
||
func TestSchedulerUnsafeVarAPI(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册不安全的 var API
|
||
RegisterSchedulerUnsafeVarAPI(L, ngx)
|
||
|
||
// 验证 ngx.var 存在
|
||
v := ngx.RawGetString("var")
|
||
if v == glua.LNil {
|
||
t.Fatal("ngx.var should exist")
|
||
}
|
||
|
||
// 尝试读取 ngx.var 应该返回错误
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
local x = ngx.var.some_var
|
||
end)
|
||
if ok then
|
||
error("reading ngx.var should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
|
||
// 尝试写入 ngx.var 应该返回错误
|
||
err = L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx.var.some_var = "value"
|
||
end)
|
||
if ok then
|
||
error("writing ngx.var should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerUnsafeCtxAPI 测试 Scheduler 模式下 ngx.ctx 不可用。
|
||
func TestSchedulerUnsafeCtxAPISeparate(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册不安全的 ctx API
|
||
RegisterSchedulerUnsafeCtxAPI(L, ngx)
|
||
|
||
// 验证 ngx.ctx 存在
|
||
ctx := ngx.RawGetString("ctx")
|
||
if ctx == glua.LNil {
|
||
t.Fatal("ngx.ctx should exist")
|
||
}
|
||
|
||
// 尝试读取 ngx.ctx 应该返回错误
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
local x = ngx.ctx.some_key
|
||
end)
|
||
if ok then
|
||
error("reading ngx.ctx should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
|
||
// 尝试写入 ngx.ctx 应该返回错误
|
||
err = L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx.ctx.some_key = "value"
|
||
end)
|
||
if ok then
|
||
error("writing ngx.ctx should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerUnsafeLogAPI 测试 Scheduler 模式下 ngx.log 可用。
|
||
func TestSchedulerUnsafeLogAPI(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册 Scheduler 日志 API
|
||
RegisterSchedulerLogAPI(L, ngx)
|
||
|
||
// 验证 ngx.log 存在
|
||
log := ngx.RawGetString("log")
|
||
if log == glua.LNil {
|
||
t.Fatal("ngx.log should exist")
|
||
}
|
||
|
||
// ngx.log 应该可以调用(不会报错)
|
||
err := L.DoString(`
|
||
ngx.log(ngx.ERR, "test log message")
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerUnsafeReqAPI 测试 Scheduler 模式下 ngx.req 不可用。
|
||
func TestSchedulerUnsafeReqAPISeparate(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册不安全的 req API
|
||
RegisterSchedulerUnsafeReqAPI(L, ngx)
|
||
|
||
// 验证 ngx["ngx.req"] 存在(RegisterUnsafeAPI 使用完整名称作为键)
|
||
req := ngx.RawGetString("ngx.req")
|
||
if req == glua.LNil {
|
||
t.Fatal("ngx[\"ngx.req\"] should exist")
|
||
}
|
||
|
||
// 尝试调用 ngx.req.get_method 应该返回错误
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx["ngx.req"].get_method()
|
||
end)
|
||
if ok then
|
||
error("ngx.req.get_method should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerUnsafeRespAPI 测试 Scheduler 模式下 ngx.resp 不可用。
|
||
func TestSchedulerUnsafeRespAPISeparate(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
// 注册不安全的 resp API
|
||
RegisterSchedulerUnsafeRespAPI(L, ngx)
|
||
|
||
// 验证 ngx["ngx.resp"] 存在(RegisterUnsafeAPI 使用完整名称作为键)
|
||
resp := ngx.RawGetString("ngx.resp")
|
||
if resp == glua.LNil {
|
||
t.Fatal("ngx[\"ngx.resp\"] should exist")
|
||
}
|
||
|
||
// 尝试调用 ngx.resp.get_headers 应该返回错误
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx["ngx.resp"].get_headers()
|
||
end)
|
||
if ok then
|
||
error("ngx.resp.get_headers should fail in scheduler mode")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerSharedDictAvailable 测试 Scheduler 模式下 ngx.shared 可用。
|
||
func TestSchedulerSharedDictAvailable(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
// 创建共享字典
|
||
engine.CreateSharedDict("test_dict", 100)
|
||
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
// 调用 setupSchedulerNgxAPI
|
||
setupSchedulerNgxAPI(L, engine)
|
||
|
||
// 验证 ngx.shared.DICT 存在
|
||
err = L.DoString(`
|
||
if ngx.shared == nil then
|
||
error("ngx.shared should exist")
|
||
end
|
||
local dict = ngx.shared.DICT("test_dict")
|
||
if dict == nil then
|
||
error("ngx.shared.DICT('test_dict') should return dict")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerTimerAvailable 测试 Scheduler 模式下 ngx.timer 可用。
|
||
func TestSchedulerTimerAvailable(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
// 调用 setupSchedulerNgxAPI
|
||
setupSchedulerNgxAPI(L, engine)
|
||
|
||
// 验证 ngx.timer 存在
|
||
err = L.DoString(`
|
||
if ngx.timer == nil then
|
||
error("ngx.timer should exist")
|
||
end
|
||
if ngx.timer.at == nil then
|
||
error("ngx.timer.at should exist")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerModeIsolation 测试 Scheduler 模式与普通模式隔离。
|
||
func TestSchedulerModeIsolation(t *testing.T) {
|
||
// 创建两个 LState,一个在 scheduler 模式,一个不在
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
// 普通 LState
|
||
normalL := glua.NewState()
|
||
defer normalL.Close()
|
||
|
||
// Scheduler LState
|
||
schedulerL := glua.NewState()
|
||
defer schedulerL.Close()
|
||
|
||
setupSchedulerNgxAPI(schedulerL, engine)
|
||
|
||
// 验证普通 LState 不是 scheduler 模式
|
||
if IsSchedulerMode(normalL) {
|
||
t.Error("Normal LState should not be in scheduler mode")
|
||
}
|
||
|
||
// 验证 Scheduler LState 是 scheduler 模式
|
||
if !IsSchedulerMode(schedulerL) {
|
||
t.Error("Scheduler LState should be in scheduler mode")
|
||
}
|
||
}
|
||
|
||
// TestSchedulerErrorMessages 测试 Scheduler 模式下错误消息格式。
|
||
func TestSchedulerErrorMessages(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
RegisterSchedulerUnsafeLocationAPI(L, ngx)
|
||
RegisterSchedulerUnsafeVarAPI(L, ngx)
|
||
RegisterSchedulerUnsafeCtxAPI(L, ngx)
|
||
|
||
// 测试 location 错误消息
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
ngx.location.capture("/test")
|
||
end)
|
||
if not string.find(err, "not available") then
|
||
error("Error message should mention 'not available': " .. tostring(err))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
|
||
// 测试 var 错误消息
|
||
err = L.DoString(`
|
||
local ok, err = pcall(function()
|
||
local x = ngx.var.test
|
||
end)
|
||
if not string.find(err, "not available") then
|
||
error("Error message should mention 'not available': " .. tostring(err))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
|
||
// 测试 ctx 错误消息
|
||
err = L.DoString(`
|
||
local ok, err = pcall(function()
|
||
local x = ngx.ctx.test
|
||
end)
|
||
if not string.find(err, "not available") then
|
||
error("Error message should mention 'not available': " .. tostring(err))
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerMultipleUnsafeCalls 测试多次调用不安全 API。
|
||
func TestSchedulerMultipleUnsafeCalls(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
RegisterSchedulerUnsafeVarAPI(L, ngx)
|
||
|
||
// 多次调用都应该失败
|
||
err := L.DoString(`
|
||
for i = 1, 5 do
|
||
local ok, err = pcall(function()
|
||
ngx.var["key" .. i] = "value" .. i
|
||
end)
|
||
if ok then
|
||
error("Call " .. i .. " should have failed")
|
||
end
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerNilValueHandling 测试 Scheduler 模式下处理 nil 值。
|
||
func TestSchedulerNilValueHandling(t *testing.T) {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
|
||
RegisterSchedulerUnsafeVarAPI(L, ngx)
|
||
|
||
// 尝试读取 nil 键
|
||
err := L.DoString(`
|
||
local ok, err = pcall(function()
|
||
local x = ngx.var[nil]
|
||
end)
|
||
if ok then
|
||
error("Reading nil key should fail")
|
||
end
|
||
`)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
// TestSchedulerConcurrentAccess 测试并发访问 Scheduler API。
|
||
func TestSchedulerConcurrentAccess(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
// 创建多个 LState 并发访问
|
||
done := make(chan bool, 10)
|
||
|
||
for range 10 {
|
||
go func() {
|
||
L := glua.NewState()
|
||
defer L.Close()
|
||
|
||
setupSchedulerNgxAPI(L, engine)
|
||
|
||
// 验证 scheduler 模式
|
||
if !IsSchedulerMode(L) {
|
||
t.Error("Should be in scheduler mode")
|
||
}
|
||
|
||
done <- true
|
||
}()
|
||
}
|
||
|
||
// 等待所有 goroutine 完成
|
||
for range 10 {
|
||
<-done
|
||
}
|
||
}
|