lolly/internal/lua/scheduler_test.go
xfy f145a8770e refactor: modernize code with Go 1.22+ features
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>
2026-04-30 10:37:45 +08:00

484 lines
11 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 提供 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
}
}