Batch 1 - Crash/data corruption: - Add nil/empty config guards in server and app - Protect against fasthttp buffer mutation in logging - Ensure mimeutil falls back before caching empty MIME - Make proxy health checker and resolver tolerate nil cfg - Prevent Lua EMERG/ALERT/CRIT log levels from killing process Batch 2 - Concurrency/race conditions: - Add focused race tests for compression pool, ssl/ocsp, server proxies slice, Lua timer cancel, and TCP socket connect - Fix Lua timer active race and socket ConnectAsync race Batch 3 - Resource leaks/functional damage: - Track and stop rate limiter cleanup goroutines on shutdown - Add tests for proxy connection count leaks, server pool deadlock, Linux sendfile integrity, WebSocket frame data loss, stream UDP stop deadlock, and upstream name mismatch - Fix stream ListenTCP/upstream lookup and UDP shutdown Batch 4 - High severity technical debt: - Add nil guard in security headers middleware - Validate sliding window divisor and add tests - Protect static handler fields with existing RWMutex - Avoid mutating live HostClient.Addr on DNS updates - Fix slow start Start/Stop and resolver restart - Initialize variable fallback context maps - Add tests for request_id/time_local and logging file handle close Also: - Relax integration variable performance threshold under race detector - Update benchmark plan docs for new stream ListenTCP signature Verification: - make test: pass - go test -race -count=1 ./internal/...: pass - make lint: 0 issues - make build: success
188 lines
4.4 KiB
Go
188 lines
4.4 KiB
Go
// Package lua 提供 Lua 脚本嵌入能力
|
||
package lua
|
||
|
||
import (
|
||
"sync"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
glua "github.com/yuin/gopher-lua"
|
||
)
|
||
|
||
func TestTimerManagerAt(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
manager := engine.TimerManager()
|
||
require.NotNil(t, manager)
|
||
|
||
// 创建 Lua 函数作为回调
|
||
L := engine.GetLStateForTest()
|
||
defer engine.PutLStateForTest(L)
|
||
|
||
// 注册一个简单的回调函数
|
||
callback := L.NewFunction(func(_ *glua.LState) int {
|
||
return 0
|
||
})
|
||
|
||
// 创建定时器
|
||
handle, err := manager.At(100*time.Millisecond, callback, nil)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, handle)
|
||
|
||
// 等待定时器触发
|
||
time.Sleep(200 * time.Millisecond)
|
||
|
||
// 定时器应该已完成(active count 回到 0)
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
}
|
||
|
||
func TestTimerManagerCancel(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
manager := engine.TimerManager()
|
||
|
||
L := engine.GetLStateForTest()
|
||
defer engine.PutLStateForTest(L)
|
||
callback := L.NewFunction(func(_ *glua.LState) int {
|
||
return 0
|
||
})
|
||
|
||
// 创建定时器
|
||
handle, err := manager.At(200*time.Millisecond, callback, nil)
|
||
require.NoError(t, err)
|
||
|
||
// 立即取消
|
||
ok := manager.Cancel(handle)
|
||
assert.True(t, ok)
|
||
|
||
// 等待超过定时器时间
|
||
time.Sleep(300 * time.Millisecond)
|
||
|
||
// 定时器应该被取消,active count 为 0
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
}
|
||
|
||
func TestTimerManagerWaitAll(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
manager := engine.TimerManager()
|
||
|
||
L := engine.GetLStateForTest()
|
||
|
||
// 创建多个定时器
|
||
for range 3 {
|
||
callback := L.NewFunction(func(_ *glua.LState) int {
|
||
return 0
|
||
})
|
||
manager.At(50*time.Millisecond, callback, nil)
|
||
}
|
||
|
||
// 等待所有完成
|
||
ok := manager.WaitAll(1 * time.Second)
|
||
assert.True(t, ok)
|
||
|
||
// active count 应该回到 0
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
}
|
||
|
||
func TestTimerLuaAPI(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
L := engine.GetLStateForTest()
|
||
defer engine.PutLStateForTest(L)
|
||
|
||
// 注册 ngx.timer API
|
||
ngx := L.NewTable()
|
||
L.SetGlobal("ngx", ngx)
|
||
RegisterTimerAPI(L, engine.TimerManager(), ngx)
|
||
|
||
// 测试 ngx.timer.at
|
||
err = L.DoString(`
|
||
-- 创建无 upvalue 的定时器(回调不能捕获外部变量)
|
||
local handle, err = ngx.timer.at(0.1, function()
|
||
-- callback body (no upvalues)
|
||
end)
|
||
|
||
assert(handle ~= nil)
|
||
assert(err == nil)
|
||
|
||
-- 检查 running_count
|
||
local running = ngx.timer.running_count()
|
||
assert(running >= 1)
|
||
`)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
func TestTimerRunningCount(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
manager := engine.TimerManager()
|
||
|
||
// 初始应该为 0
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
|
||
// 创建定时器
|
||
callback := engine.GetLStateForTest().NewFunction(func(_ *glua.LState) int {
|
||
return 0
|
||
})
|
||
|
||
handle, _ := manager.At(50*time.Millisecond, callback, nil)
|
||
_ = handle
|
||
|
||
// 刚创建后应该有活跃定时器(在定时器触发前)
|
||
// 注意:由于简化实现,定时器执行很快,所以 active count 可能很快回到 0
|
||
// 这里我们只验证定时器最终会完成
|
||
|
||
// 等待完成
|
||
time.Sleep(100 * time.Millisecond)
|
||
|
||
// 应该回到 0
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
}
|
||
|
||
// TestTimerManager_CancelConcurrent 验证并发 Cancel 与定时器触发不会产生负 active 计数或 panic。
|
||
func TestTimerManager_CancelConcurrent(t *testing.T) {
|
||
engine, err := NewEngine(DefaultConfig())
|
||
require.NoError(t, err)
|
||
defer engine.Close()
|
||
|
||
manager := engine.TimerManager()
|
||
|
||
L := engine.GetLStateForTest()
|
||
defer engine.PutLStateForTest(L)
|
||
|
||
var wg sync.WaitGroup
|
||
for range 100 {
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
callback := L.NewFunction(func(_ *glua.LState) int {
|
||
return 0
|
||
})
|
||
handle, err := manager.At(10*time.Millisecond, callback, nil)
|
||
require.NoError(t, err)
|
||
manager.Cancel(handle)
|
||
}()
|
||
}
|
||
wg.Wait()
|
||
|
||
// 等待所有定时器触发/取消完成
|
||
time.Sleep(200 * time.Millisecond)
|
||
|
||
// active 计数不应为负
|
||
require.GreaterOrEqual(t, manager.ActiveCount(), int32(0))
|
||
assert.Equal(t, int32(0), manager.ActiveCount())
|
||
}
|