Merge branch 'fix-sleep' into 'master'

refactor: unify float64 conversion logic in ToolSleep and ToolSleepMS, enhance error logging

See merge request iesqa/httprunner!152
This commit is contained in:
李隆
2025-08-12 06:53:15 +00:00
4 changed files with 80 additions and 85 deletions

View File

@@ -217,6 +217,8 @@ func Interface2Float64(i interface{}) (float64, error) {
case string: // e.g. "1", "0.5"
floatVar, err := strconv.ParseFloat(v, 64)
if err != nil {
log.Error().Err(err).Str("value", v).
Msg("convert string to float64 failed")
return 0, err
}
return floatVar, nil
@@ -226,6 +228,10 @@ func Interface2Float64(i interface{}) (float64, error) {
if ok {
return value.Float64()
}
// Log error for unsupported types
log.Error().Interface("value", i).Type("type", i).
Msg("convert float64 failed")
return 0, errors.New("failed to convert interface to float64")
}
@@ -334,29 +340,6 @@ func IsZeroFloat64(f float64) bool {
return math.Abs(f) < threshold
}
func ConvertToFloat64(val interface{}) (float64, error) {
switch v := val.(type) {
case float64:
return v, nil
case int:
return float64(v), nil
case int64:
return float64(v), nil
case string:
f, err := strconv.ParseFloat(v, 64)
if err != nil {
log.Error().Err(err).Str("value", v).
Msg("convert string to float64 failed")
return 0, err
}
return f, nil
default:
log.Error().Interface("value", val).Type("type", val).
Msg("convert float64 failed")
return 0, errors.New("convert float64 error")
}
}
func ConvertToFloat64Slice(val interface{}) ([]float64, error) {
if paramsSlice, ok := val.([]float64); ok {
return paramsSlice, nil
@@ -369,7 +352,7 @@ func ConvertToFloat64Slice(val interface{}) ([]float64, error) {
var err error
float64Slice := make([]float64, len(paramsSlice))
for i, v := range paramsSlice {
float64Slice[i], err = ConvertToFloat64(v)
float64Slice[i], err = Interface2Float64(v)
if err != nil {
return nil, err
}

View File

@@ -1 +1 @@
v5.0.0-250811
v5.0.0-250812

View File

@@ -2,9 +2,7 @@ package uixt
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/mark3labs/mcp-go/mcp"
@@ -70,28 +68,12 @@ func (t *ToolSleep) Implement() server.ToolHandlerFunc {
// Sleep action logic
log.Info().Interface("seconds", seconds).Msg("sleeping")
var duration time.Duration
var actualSeconds float64
switch v := seconds.(type) {
case float64:
actualSeconds = v
duration = time.Duration(v*1000) * time.Millisecond
case int:
actualSeconds = float64(v)
duration = time.Duration(v) * time.Second
case int64:
actualSeconds = float64(v)
duration = time.Duration(v) * time.Second
case string:
s, err := builtin.ConvertToFloat64(v)
if err != nil {
return nil, fmt.Errorf("invalid sleep duration: %v", v)
}
actualSeconds = s
duration = time.Duration(s*1000) * time.Millisecond
default:
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
// Use Interface2Float64 for unified type conversion
actualSeconds, err := builtin.Interface2Float64(seconds)
if err != nil {
return nil, fmt.Errorf("invalid sleep duration: %v", seconds)
}
duration := time.Duration(actualSeconds) * time.Second
// Extract start_time_ms and use sleepStrict for unified sleep logic
startTime, err := extractStartTimeMs(request)
@@ -116,19 +98,19 @@ func (t *ToolSleep) ConvertActionToCallToolRequest(action option.MobileAction) (
arguments := map[string]any{}
var seconds float64
if param, ok := action.Params.(json.Number); ok {
seconds, _ = param.Float64()
arguments["seconds"] = seconds
} else if param, ok := action.Params.(int64); ok {
seconds = float64(param)
arguments["seconds"] = seconds
} else if sleepConfig, ok := action.Params.(SleepConfig); ok {
if sleepConfig, ok := action.Params.(SleepConfig); ok {
// When startTime is provided, pass both seconds and startTime
seconds = sleepConfig.Seconds
arguments["seconds"] = seconds
arguments["start_time_ms"] = sleepConfig.StartTime.UnixMilli()
} else {
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep params: %v", action.Params)
// Use builtin.Interface2Float64 for unified parameter handling
var err error
seconds, err = builtin.Interface2Float64(action.Params)
if err != nil {
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep params: %v", action.Params)
}
arguments["seconds"] = seconds
}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
@@ -167,28 +149,13 @@ func (t *ToolSleepMS) Implement() server.ToolHandlerFunc {
// Sleep MS action logic
log.Info().Interface("milliseconds", milliseconds).Msg("sleeping in milliseconds")
var duration time.Duration
var actualMilliseconds int64
switch v := milliseconds.(type) {
case float64:
actualMilliseconds = int64(v)
duration = time.Duration(v) * time.Millisecond
case int:
actualMilliseconds = int64(v)
duration = time.Duration(v) * time.Millisecond
case int64:
actualMilliseconds = v
duration = time.Duration(v) * time.Millisecond
case string:
ms, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid sleep duration: %v", v)
}
actualMilliseconds = ms
duration = time.Duration(ms) * time.Millisecond
default:
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
// Use Interface2Float64 for unified type conversion, then convert to int64
floatVal, err := builtin.Interface2Float64(milliseconds)
if err != nil {
return nil, fmt.Errorf("invalid sleep duration: %v", milliseconds)
}
actualMilliseconds := int64(floatVal)
duration := time.Duration(actualMilliseconds) * time.Millisecond
// Extract start_time_ms and use sleepStrict for unified sleep logic
startTime, err := extractStartTimeMs(request)
@@ -212,19 +179,19 @@ func (t *ToolSleepMS) ConvertActionToCallToolRequest(action option.MobileAction)
arguments := map[string]any{}
var milliseconds int64
if param, ok := action.Params.(json.Number); ok {
milliseconds, _ = param.Int64()
arguments["milliseconds"] = milliseconds
} else if param, ok := action.Params.(int64); ok {
milliseconds = param
arguments["milliseconds"] = milliseconds
} else if sleepConfig, ok := action.Params.(SleepConfig); ok {
if sleepConfig, ok := action.Params.(SleepConfig); ok {
// When startTime is provided, pass both milliseconds and startTime
milliseconds = sleepConfig.Milliseconds
arguments["milliseconds"] = milliseconds
arguments["start_time_ms"] = sleepConfig.StartTime.UnixMilli()
} else {
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep ms params: %v", action.Params)
// Use builtin.Interface2Float64 for unified parameter handling, then convert to int64
floatVal, err := builtin.Interface2Float64(action.Params)
if err != nil {
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep ms params: %v", action.Params)
}
milliseconds = int64(floatVal)
arguments["milliseconds"] = milliseconds
}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil

View File

@@ -30,6 +30,15 @@ func TestToolSleep_ConvertActionToCallToolRequest(t *testing.T) {
expectedArgs: map[string]any{"seconds": float64(3.5)},
shouldError: false,
},
{
name: "float64 parameter",
action: option.MobileAction{
Method: option.ACTION_Sleep,
Params: float64(5.2),
},
expectedArgs: map[string]any{"seconds": float64(5.2)},
shouldError: false,
},
{
name: "int64 parameter",
action: option.MobileAction{
@@ -63,6 +72,24 @@ func TestToolSleep_ConvertActionToCallToolRequest(t *testing.T) {
expectedArgs: nil,
shouldError: true,
},
{
name: "json.Number with integer value",
action: option.MobileAction{
Method: option.ACTION_Sleep,
Params: json.Number("10"),
},
expectedArgs: map[string]any{"seconds": float64(10)},
shouldError: false,
},
{
name: "json.Number with decimal value",
action: option.MobileAction{
Method: option.ACTION_Sleep,
Params: json.Number("1.25"),
},
expectedArgs: map[string]any{"seconds": float64(1.25)},
shouldError: false,
},
}
for _, tt := range tests {
@@ -109,6 +136,15 @@ func TestToolSleepMS_ConvertActionToCallToolRequest(t *testing.T) {
expectedArgs: map[string]any{"milliseconds": int64(2000)},
shouldError: false,
},
{
name: "float64 parameter",
action: option.MobileAction{
Method: option.ACTION_SleepMS,
Params: float64(2500.7),
},
expectedArgs: map[string]any{"milliseconds": int64(2500)},
shouldError: false,
},
{
name: "SleepConfig with startTime",
action: option.MobileAction{
@@ -124,6 +160,15 @@ func TestToolSleepMS_ConvertActionToCallToolRequest(t *testing.T) {
},
shouldError: false,
},
{
name: "json.Number with decimal value",
action: option.MobileAction{
Method: option.ACTION_SleepMS,
Params: json.Number("1234.56"),
},
expectedArgs: map[string]any{"milliseconds": int64(1234)},
shouldError: false,
},
{
name: "invalid parameter type",
action: option.MobileAction{