Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1,022 changes: 1,022 additions & 0 deletions dialtesting/grpc.go

Large diffs are not rendered by default.

140 changes: 140 additions & 0 deletions dialtesting/grpc_script.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the MIT License.
// This product includes software developed at Guance Cloud (https://www.guance.com/).
// Copyright 2021-present Guance, Inc.

package dialtesting

import (
"encoding/json"
"fmt"
"time"

"github.com/GuanceCloud/cliutils/point"
"github.com/GuanceCloud/pipeline-go/lang"
"github.com/GuanceCloud/pipeline-go/lang/platypus"
"github.com/GuanceCloud/pipeline-go/ptinput"
)

type ScriptGRPCRequestResponse struct {
Body string `json:"body"`
}

func (h *ScriptGRPCRequestResponse) String() (string, error) {
bytes, err := json.Marshal(h)
if err != nil {
return "", fmt.Errorf("response marshal failed: %w", err)
}
return string(bytes), nil
}

type ScriptGRPCMessage struct {
Response *ScriptGRPCRequestResponse `json:"response"`
Vars *Vars `json:"vars"`
}

// postScriptDoGRPC run pipeline script for gRPC response and return result.
//
// bodyBytes is the JSON body of the gRPC response.
func postScriptDoGRPC(script string, bodyBytes []byte) (*ScriptResult, error) {
if script == "" || bodyBytes == nil {
return &ScriptResult{}, nil
}

response := &ScriptGRPCRequestResponse{
Body: string(bodyBytes),
}

result, err := runPipelineGRPC(script, response, nil)
if err != nil {
return nil, fmt.Errorf("run pipeline failed: %w", err)
}
return result, nil
}

func runPipelineGRPC(script string, response *ScriptGRPCRequestResponse, vars *Vars) (*ScriptResult, error) {
scriptName := "script"

script = fmt.Sprintf(`
content = load_json(_)
response = content["response"]
vars = content["vars"]
result = {}

%s

add_key(result, result)
add_key(vars, vars)
`, script)

pls, errs := platypus.NewScripts(
map[string]string{scriptName: script},
lang.WithCat(point.Logging),
)

defer func() {
for _, pl := range pls {
pl.Cleanup()
}
}()

for k, v := range errs {
return nil, fmt.Errorf("new scripts failed: %s, %w", k, v)
}

pl, ok := pls[scriptName]
if !ok {
return nil, fmt.Errorf("script %s not found", scriptName)
}

if vars == nil {
vars = &Vars{}
}

message := &ScriptGRPCMessage{
Response: response,
Vars: vars,
}

messageBytes, err := json.Marshal(message)
if err != nil {
return nil, fmt.Errorf("message marshal failed: %w", err)
}
messageString := string(messageBytes)

fileds := map[string]interface{}{
"message": messageString,
}

pt := ptinput.NewPlPoint(point.Logging, "test", nil, fileds, time.Now())

if err := pl.Run(pt, nil, nil); err != nil {
return nil, fmt.Errorf("run failed: %w", err)
}

resultFields := pt.Fields()

result := ScriptHTTPResult{}

if val, ok := resultFields["result"]; !ok {
return nil, fmt.Errorf("result not found")
} else if err := json.Unmarshal([]byte(getFiledString(val)), &result); err != nil {
return nil, fmt.Errorf("unmarshal result failed: %w", err)
}

if val, ok := resultFields["vars"]; !ok {
return nil, fmt.Errorf("vars not found")
} else if err := json.Unmarshal([]byte(getFiledString(val)), &vars); err != nil {
return nil, fmt.Errorf("unmarshal vars failed: %w", err)
}

// limit error message length
if len(result.ErrorMessage) > MaxErrorMessageSize {
result.ErrorMessage = result.ErrorMessage[:MaxErrorMessageSize] + "..."
}

return &ScriptResult{
Result: result,
Vars: *vars,
}, nil
}
141 changes: 141 additions & 0 deletions dialtesting/grpc_script_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the MIT License.
// This product includes software developed at Guance Cloud (https://www.guance.com/).
// Copyright 2021-present Guance, Inc.

package dialtesting

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestPostScriptDoGRPC(t *testing.T) {
t.Run("success - extract message field", func(t *testing.T) {
script := `
body = load_json(response["body"])
vars["message"] = body["message"]
result["is_failed"] = false
`

body := []byte(`{"message":"你好, test! 这是来自 gRPC 的问候"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.False(t, result.Result.IsFailed)
assert.Equal(t, "你好, test! 这是来自 gRPC 的问候", result.Vars["message"])
})

t.Run("success - extract multiple fields", func(t *testing.T) {
script := `
body = load_json(response["body"])
vars["message"] = body["message"]
vars["status"] = body["status"]
result["is_failed"] = false
`

body := []byte(`{"message":"hello","status":"ok"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.False(t, result.Result.IsFailed)
assert.Equal(t, "hello", result.Vars["message"])
assert.Equal(t, "ok", result.Vars["status"])
})

t.Run("failure - missing required field", func(t *testing.T) {
script := `
body = load_json(response["body"])
if body["message"] != nil {
vars["message"] = body["message"]
result["is_failed"] = false
} else {
result["is_failed"] = true
result["error_message"] = "响应中缺少 message 字段"
}
`

body := []byte(`{"status":"ok"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.True(t, result.Result.IsFailed)
assert.Equal(t, "响应中缺少 message 字段", result.Result.ErrorMessage)
})

t.Run("failure - custom error", func(t *testing.T) {
script := `
result["is_failed"] = true
result["error_message"] = "custom error message"
`

body := []byte(`{"message":"hello"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.True(t, result.Result.IsFailed)
assert.Equal(t, "custom error message", result.Result.ErrorMessage)
})

t.Run("empty script", func(t *testing.T) {
script := ""

body := []byte(`{"message":"hello"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
})

t.Run("nil body", func(t *testing.T) {
script := `
vars["test"] = "value"
result["is_failed"] = false
`

result, err := postScriptDoGRPC(script, nil)
assert.NoError(t, err)
assert.NotNil(t, result)
})

t.Run("invalid JSON in response body", func(t *testing.T) {
script := `
body = load_json(response["body"])
if body != nil {
vars["message"] = body["message"]
result["is_failed"] = false
} else {
result["is_failed"] = true
result["error_message"] = "invalid JSON"
}
`

body := []byte(`invalid json`)

result, _ := postScriptDoGRPC(script, body)
assert.NotNil(t, result)
})

t.Run("complex nested JSON", func(t *testing.T) {
script := `
body = load_json(response["body"])
vars["user_name"] = body["user"]["name"]
vars["user_age"] = body["user"]["age"]
result["is_failed"] = false
`

body := []byte(`{"user":{"name":"test","age":25},"status":"ok"}`)

result, err := postScriptDoGRPC(script, body)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.False(t, result.Result.IsFailed)
assert.Equal(t, "test", result.Vars["user_name"])
assert.Equal(t, float64(25), result.Vars["user_age"]) // JSON 数字会被解析为 float64
})
}
Loading