Skip to content

Commit

Permalink
Merge pull request #10 from vearne/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
vearne authored May 14, 2024
2 parents 993e6d9 + c3d664e commit 6b93b45
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 1 deletion.
9 changes: 8 additions & 1 deletion config_files/my_http_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@
- name: "HttpBodyEqualRule"
xpath: "/author"
Expected: "book3_author-2"

- name: "HttpLuaRule"
lua: |
function verify(r)
local json = require "json";
local book = json.decode(r:body());
return book.title == "book3_title" and book.author == "book3_author-2";
end
- id: 4
desc: "delete the book1"
request:
Expand Down
Empty file added doc/lua.md
Empty file.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/vearne/executor v0.0.3
github.com/vearne/simplelog v0.0.2
github.com/vearne/zaplog v0.0.2
github.com/yuin/gopher-lua v1.1.1
go.uber.org/zap v1.27.0
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -39,4 +40,5 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ github.com/vearne/zaplog v0.0.2/go.mod h1:p8k2zw85seZZHEUfz5D6JWtbaf41oqQav8ibm4
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
Expand Down Expand Up @@ -136,3 +138,5 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0=
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc=
5 changes: 5 additions & 0 deletions internal/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strings"
"sync"
"sync/atomic"
"time"
)

var GlobalConfig config.AutoTestConfig
Expand Down Expand Up @@ -55,6 +56,10 @@ func ParseConfigFile(filePath string) error {
return err
}

if GlobalConfig.Global.RequestTimeout < time.Second {
GlobalConfig.Global.RequestTimeout = time.Second
}

slog.Info("2) parse http rule files")
// read testcase
// 1) http testcase
Expand Down
127 changes: 127 additions & 0 deletions internal/rule/http_lua_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package rule

import (
"github.com/go-resty/resty/v2"
"github.com/vearne/zaplog"
"github.com/yuin/gopher-lua"
"go.uber.org/zap"
luajson "layeh.com/gopher-json"
"strconv"
"sync"
)

var L *lua.LState

/*
Lua virtual machine is not thread-safe
so we need LuaVMLock to protect L
*/
var LuaVMLock sync.Mutex

func init() {
L = lua.NewState()
//defer L.Close()
// register json lib
luajson.Preload(L)
registerHttpRespType(L)
}

type HttpResp struct {
Code string
Body string
}

const luaHttpRespTypeName = "HttpResp"

var httpRespMethods = map[string]lua.LGFunction{
"code": getSetCode,
"body": getSetBody,
}

// Getter and setter for the HttpResp#Code
func getSetCode(L *lua.LState) int {
p := checkHttpResp(L)
if L.GetTop() == 3 {
p.Code = L.CheckString(2)
return 0
}
L.Push(lua.LString(p.Code))
return 1
}

// Getter and setter for the HttpResp#Body
func getSetBody(L *lua.LState) int {
p := checkHttpResp(L)
if L.GetTop() == 3 {
p.Body = L.CheckString(3)
return 0
}
L.Push(lua.LString(p.Body))
return 1
}

// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkHttpResp(L *lua.LState) *HttpResp {
ud := L.CheckUserData(1)
if v, ok := ud.Value.(*HttpResp); ok {
return v
}
L.ArgError(1, "HttpResp expected")
return nil
}

// Constructor
func newHttpResp(L *lua.LState) int {
resp := &HttpResp{L.CheckString(1), L.CheckString(2)}
ud := L.NewUserData()
ud.Value = resp
L.SetMetatable(ud, L.GetTypeMetatable(luaHttpRespTypeName))
L.Push(ud)
return 1
}

// Registers my person type to given L.
func registerHttpRespType(L *lua.LState) {
mt := L.NewTypeMetatable(luaHttpRespTypeName)
L.SetGlobal("HttpResp", mt)
// static attributes
L.SetField(mt, "new", L.NewFunction(newHttpResp))
// methods
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), httpRespMethods))
}

type HttpLuaRule struct {
LuaStr string `json:"lua"`
}

func (r *HttpLuaRule) Name() string {
return "HttpLuaRule"
}

func (r *HttpLuaRule) Verify(resp *resty.Response) bool {
L.SetGlobal("codeStr", lua.LString(strconv.Itoa(resp.StatusCode())))
L.SetGlobal("bodyStr", lua.LString(resp.String()))

source := r.LuaStr +
`
r = HttpResp.new(codeStr, bodyStr);
return verify(r);
`
if err := runLuaStr(source); err != nil {
zaplog.Error("HttpLuaRule-Verify",
zap.Int("status", resp.StatusCode()),
zap.String("body", resp.String()),
zap.String("LuaStr", r.LuaStr),
zap.Error(err))
return false
}
lv := L.Get(-1)
return lv == lua.LTrue
}

func runLuaStr(source string) error {
LuaVMLock.Lock()
defer LuaVMLock.Unlock()

return L.DoString(source)
}
43 changes: 43 additions & 0 deletions internal/rule/http_lua_rule_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package rule

import (
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
lua "github.com/yuin/gopher-lua"
"testing"
)

func TestLua(t *testing.T) {
if err := runLuaStr(`
function verify(r)
local json = require "json";
local car = json.decode(r:body());
return r:code() == "200" and car.age == 10 and car.name == "buick";
end
r = HttpResp.new("200", "{\"age\": 10,\"name\": \"buick\"}")
return verify(r)
`); err != nil {
panic(err)
}

result := false
lv := L.Get(-1)
if lv == lua.LTrue {
result = true
}
assert.True(t, result)
}

func TestHttpLuaRule(t *testing.T) {
luaStr := `
function verify(r)
local json = require "json";
local person = json.decode(r:body());
return person.age == 10 and person.name == "John";
end
`
var resp resty.Response
resp.SetBody([]byte(`{"age": 10, "name": "John"}`))
rule := HttpLuaRule{LuaStr: luaStr}
assert.True(t, rule.Verify(&resp))
}

0 comments on commit 6b93b45

Please sign in to comment.