// Package lua 提供 Lua 中间件测试 package lua import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) // TestLuaMiddlewareCreation 测试 LuaMiddleware 创建 func TestLuaMiddlewareCreation(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 创建临时脚本文件 tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte("ngx.say('hello')"), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, Timeout: 10 * time.Second, Name: "test-middleware", Enabled: true, } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) require.NotNil(t, middleware) assert.Equal(t, "test-middleware", middleware.Name()) assert.Equal(t, scriptPath, middleware.GetScriptPath()) assert.Equal(t, PhaseContent, middleware.GetPhase()) assert.Equal(t, 10*time.Second, middleware.timeout) assert.True(t, middleware.IsEnabled()) } // TestLuaMiddlewareValidation 测试配置验证 func TestLuaMiddlewareValidation(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 缺少 engine config := LuaMiddlewareConfig{ScriptPath: "test.lua"} middleware, err := NewLuaMiddleware(nil, config) assert.Error(t, err) assert.Nil(t, middleware) assert.Contains(t, err.Error(), "engine is required") // 缺少 script path middleware, err = NewLuaMiddleware(engine, LuaMiddlewareConfig{}) assert.Error(t, err) assert.Nil(t, middleware) assert.Contains(t, err.Error(), "script path is required") } // TestLuaMiddlewareProcess 测试中间件处理 func TestLuaMiddlewareProcess(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 创建脚本文件 tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte(`ngx.say("hello from lua")`), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, Enabled: true, EnabledSet: true, // 显式启用 } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) // 创建 RequestCtx ctx := &fasthttp.RequestCtx{} // 创建最终处理器 finalHandler := func(ctx *fasthttp.RequestCtx) { ctx.WriteString("final handler") } // 包装处理器 handler := middleware.Process(finalHandler) // 执行 handler(ctx) // 验证输出包含 Lua 输出 body := string(ctx.Response.Body()) assert.Contains(t, body, "hello from lua") } // TestLuaMiddlewareDisabled 测试禁用的中间件 func TestLuaMiddlewareDisabled(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 创建脚本文件 tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte(`ngx.say("lua output")`), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, Enabled: false, // 禁用 EnabledSet: true, // 显式设置 } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) // 创建 RequestCtx ctx := &fasthttp.RequestCtx{} // 创建最终处理器 finalHandler := func(ctx *fasthttp.RequestCtx) { ctx.WriteString("final only") } // 包装处理器 handler := middleware.Process(finalHandler) // 执行 handler(ctx) // 禁用时只执行最终处理器 body := string(ctx.Response.Body()) assert.Equal(t, "final only", body) assert.NotContains(t, body, "lua output") } // TestMultiPhaseLuaMiddlewareCreation 测试多阶段中间件创建 func TestMultiPhaseLuaMiddlewareCreation(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() multi := NewMultiPhaseLuaMiddleware(engine, "multi-test") assert.Equal(t, "multi-test", multi.Name()) assert.Equal(t, 0, multi.PhaseCount()) } // TestMultiPhaseLuaMiddlewareAddPhase 测试添加阶段 func TestMultiPhaseLuaMiddlewareAddPhase(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() multi := NewMultiPhaseLuaMiddleware(engine, "multi-test") // 添加 rewrite 阶段 rewriteScript := filepath.Join(tmpDir, "rewrite.lua") err = os.WriteFile(rewriteScript, []byte("ngx.var.uri = '/rewritten'"), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseRewrite, rewriteScript, 10*time.Second) require.NoError(t, err) assert.Equal(t, 1, multi.PhaseCount()) assert.True(t, multi.HasPhase(PhaseRewrite)) // 添加 access 阶段 accessScript := filepath.Join(tmpDir, "access.lua") err = os.WriteFile(accessScript, []byte("ngx.exit(403)"), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseAccess, accessScript, 10*time.Second) require.NoError(t, err) assert.Equal(t, 2, multi.PhaseCount()) assert.True(t, multi.HasPhase(PhaseAccess)) } // TestMultiPhaseLuaMiddlewareRemovePhase 测试移除阶段 func TestMultiPhaseLuaMiddlewareRemovePhase(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() multi := NewMultiPhaseLuaMiddleware(engine, "multi-test") scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte("return 1"), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseRewrite, scriptPath, 10*time.Second) require.NoError(t, err) assert.Equal(t, 1, multi.PhaseCount()) // 移除阶段 multi.RemovePhase(PhaseRewrite) assert.Equal(t, 0, multi.PhaseCount()) assert.False(t, multi.HasPhase(PhaseRewrite)) } // TestMultiPhaseLuaMiddlewareProcess 测试多阶段执行 func TestMultiPhaseLuaMiddlewareProcess(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() multi := NewMultiPhaseLuaMiddleware(engine, "multi-test") // rewrite 阎段脚本 rewriteScript := filepath.Join(tmpDir, "rewrite.lua") err = os.WriteFile(rewriteScript, []byte(`ngx.say("rewrite")`), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseRewrite, rewriteScript, 10*time.Second) require.NoError(t, err) // content 阎段脚本 contentScript := filepath.Join(tmpDir, "content.lua") err = os.WriteFile(contentScript, []byte(`ngx.say("content")`), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseContent, contentScript, 10*time.Second) require.NoError(t, err) // 创建 RequestCtx ctx := &fasthttp.RequestCtx{} // 创建最终处理器 finalHandler := func(ctx *fasthttp.RequestCtx) { ctx.WriteString("final") } // 包装处理器 handler := multi.Process(finalHandler) // 执行 handler(ctx) // 验证执行顺序:rewrite -> content -> final body := string(ctx.Response.Body()) // 注意:由于 Lua 输出会追加到响应体,顺序可能不同 assert.Contains(t, body, "rewrite") assert.Contains(t, body, "content") } // TestMultiPhaseLuaMiddlewareGetPhaseMiddleware 测试获取阶段中间件 func TestMultiPhaseLuaMiddlewareGetPhaseMiddleware(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() multi := NewMultiPhaseLuaMiddleware(engine, "multi-test") scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte("return 1"), 0o644) require.NoError(t, err) err = multi.AddPhase(PhaseRewrite, scriptPath, 10*time.Second) require.NoError(t, err) // 获取阶段中间件 middleware := multi.GetPhaseMiddleware(PhaseRewrite) require.NotNil(t, middleware) assert.Equal(t, PhaseRewrite, middleware.GetPhase()) // 获取不存在的阶段 middleware = multi.GetPhaseMiddleware(PhaseAccess) assert.Nil(t, middleware) } // TestLuaMiddlewareIntegrationWithChain 测试与 middleware chain 集成 func TestLuaMiddlewareIntegrationWithChain(t *testing.T) { // 这个测试验证 LuaMiddleware 可以与现有的 middleware 链集成 engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "test.lua") err = os.WriteFile(scriptPath, []byte(`ngx.say("lua middleware")`), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, Name: "lua-content", } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) // 验证实现了 Middleware 接口 // Name() 和 Process() 方法已实现 assert.Equal(t, "lua-content", middleware.Name()) // 创建 RequestCtx ctx := &fasthttp.RequestCtx{} // 创建处理器 handler := middleware.Process(func(ctx *fasthttp.RequestCtx) { ctx.WriteString("next") }) // 执行 handler(ctx) // 验证输出 body := string(ctx.Response.Body()) assert.Contains(t, body, "lua middleware") } // TestLuaMiddlewareExecutionError 测试执行错误处理 func TestLuaMiddlewareExecutionError(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 创建有语法错误的脚本 tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "error.lua") err = os.WriteFile(scriptPath, []byte("invalid lua syntax !!!"), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) ctx := &fasthttp.RequestCtx{} handler := middleware.Process(func(ctx *fasthttp.RequestCtx) { ctx.WriteString("final") }) handler(ctx) // 执行错误时返回 500 assert.Equal(t, fasthttp.StatusInternalServerError, ctx.Response.StatusCode()) } // TestLuaMiddlewareExit 测试 ngx.exit() 终止执行 func TestLuaMiddlewareExit(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() tmpDir := t.TempDir() scriptPath := filepath.Join(tmpDir, "exit.lua") err = os.WriteFile(scriptPath, []byte(`ngx.say("before exit"); ngx.exit(200)`), 0o644) require.NoError(t, err) config := LuaMiddlewareConfig{ ScriptPath: scriptPath, Phase: PhaseContent, } middleware, err := NewLuaMiddleware(engine, config) require.NoError(t, err) ctx := &fasthttp.RequestCtx{} nextCalled := false handler := middleware.Process(func(ctx *fasthttp.RequestCtx) { nextCalled = true ctx.WriteString("next handler") }) handler(ctx) // ngx.exit() 终止执行,next handler 不应被调用 assert.False(t, nextCalled) // 状态码应为 200 assert.Equal(t, 200, ctx.Response.StatusCode()) // 输出包含 Lua 输出 body := string(ctx.Response.Body()) assert.Contains(t, body, "before exit") }